Jupiter ACE
4000i
FORTH Programming
Steven Vickers
Published by
Jupiter* Cantab Ltd, Cambridge, England,
The Jupiter* Ace microcomputer is available in North America from
Computer Distribution Associates, Jupiter* Computer Divison, 56
South 3 rd Street, Oxford, Pennsylvania, 19363, USA.
ISBN 0 9508477 0 4
© 1982 Jupiter Cantab Ltd
First US edition 1983
* The Jupiter Ace microcomputer and this publication are not associated
with Jupiter Systems of Berkley, California.
Jupiter ACE
4000-
FORTH Programming
Steven Vickers
Contents
Chapter 1 page 5
Setting up the Ace
Chapter 2 page 8
Typing at the keyboard
How to type in your instructions to the Ace
Chapter 3 page 13
Loading programs from tape
In case you have bought some software
Chapter 4 page 16
Defining new words
How to write your own programs, using :
and ;
Chapter 5 page 19
Simple arithmetic
Integer (whole number) arithmetic and the
stack
Chapter 6 page 26
Defining new arithmetic words
Chapter 7 page 34
Altering word definitions
How to correct mistakes, using LIST, EDIT
and REDEFINE
Chapter 8 page 41
Words that are really numbers
Constants, variables and bytes in memory
Chapter 9 page 46
Making decisions
Words that can behave differently in
different circumstances, using IF, ELSE and
THEN
Chapter 10 page 54
Repeating
Words that can do the same thing over and
over again, using BEGIN and DO
Chapter 11 page 64
Sound
Using the Ace's loudspeaker, with BEEP
Chapter 12 page 69
The character set
And how to define your own characters
Chapter 13 page 77
Plotting graphs
With PLOT
Chapter 14 page 84
Saving words on tape
How to save information on cassette tape
before you turn the Ace off
Chapter 15 page 89
Fractions and decimal points
Floating point arithmetic
Chapter 16 page 94
Reading the keyboard
So that you can control a program while it is
running
Chapter 17 page 101
Other ways of counting
Decimal, binary, octal, hex and more
Chapter 18 page 107
Boolean operations
AND, OR and XOR
3
CONTENTS
Chapter 19 page 110
More advanced arithmetic
Double length arithmetic and formatted
printing
Chapter 20 page 117
Inside the dictionary
Not only your own words, but your own
ways of defining them — CREATE and
DEFINER
Chapter 21 page 124
Strings and arrays
What they are, and how to set them up
back of the Ace
Chapter 22 page 131
Vocabularies
Setting aside some words for special
Contexts
chapter 23 page 134
Inside colon definitions
How to control the compilation process, and
COMPILER
Chapter 24 page 140
How the memory is laid out
Including a list of system variables
Chapter 25 page 146
Machine code
For the Z80A processor chip
Chapter 26 page 150
Extending the Ace
How to connect your own electronics to the
Appendices page 156
A The character set
B Error codes
C The Jupiter Ace — for
reference
D Quick guide for FORTH
enthusiasts
This PDF document was created by Steve Parry-Thomas April 2006
To preserve the Users Manual for all users of the Jupiter Ace
I have tried to keep to the original manual as much as possible; there might
be the odd mistake I have not stopped. Email with any corrections you have at
steve@jupiter-ace.co.uk.
www.jupiter-ace.co.uk
4
Chapter 1
SETTING UP THE ACE
This manual is delivered with a few accessories, which you should check:
1. A Jupiter Ace computer.
2. A mains adaptor, which converts 110V AC electricity into a low voltage suitable for
the Ace. It will work properly only in certain countries (normally including the one to
which the Ace was delivered), so if you take your Ace abroad you may need a
different mains adaptor.
The power supply is a heavy plastic box, three or four inches in size, and extending
from it is a cable with a jack plug at the end.
3. A TV cable. This is a single coaxial lead with a phono plug at each end. It is used to
connect the Ace to...
4. The transfer switch box. This has a phono socket for the TV cable, and a cable to
connect it to a television.
5. A pair of cables with jack plugs at both ends, used to connect the Ace to a
cassette tape recorder. The plugs are colour-coded, so that you can tell the two leads
apart.
You will need to provide for yourself a mains electricity supply and a television, which
must work on a 525 line 60Hz UHF system. This is the standard USA system.
Later you will need a cassette tape recorder and tape, but these aren't immediately
necessary.
Having collected all these, plug the power supply into a wall socket and plug its jack
plug into the socket on the left-hand side of the Ace marked (underneath) 'POWER'.
There is no switch on the Ace, so as soon as you do this it starts working. However,
you won't know what it's doing until you connect it to the television, so that's the
next step.
Disconnect the VHF TV antenna wires from your television set (leave the UHF
wires alone) and reconnect them to the transfer switch box, at the screws marked
'TV'. Connect the wires from the transfer switch box to the VHF antenna terminal on
the TV, and use the TV cable to connect the transfer switch box to the socket on the
right-hand side of the Ace marked 'TV'.
Now plug the television into a wall socket (unless it uses batteries, of course),
switch it on, turn its volume right down, and tune it to channel 2 or 3, whichever is not
being used for broadcasting in your area. Also set the switch at the right hand back of
the computer to 2 or 3, to match the TV setting.
When you've tuned it just right the screen will be a uniform dark grey, except for a
small white square near the bottom left-hand corner.
5
Wires from
VHF TV antenna VHF antenna
terminals
Power _
TV
JDr
EAR Switch
<3 MIC box
all
Power supply
J
a
7777777777777777/777/7777
Wall sockets
TV power cable
(Now you've set it up, you can start pressing a few keys at random on the Ace
keyboard, just to see what happens. You can always get back to the starting position
by momentarily disconnecting the power supply from the Ace.)
If you have a video monitor you can use it with the Ace instead of a television. It
should be connected to the socket marked 'MONITOR'.
The Ace understands a powerful computing language called FORTH. FORTH was
invented around 1970 by Charles Moore, and was chosen for the Ace because of its
speed, its economical use of computer memory, and the way a few simple concepts
give an elegant power to the whole language.
If you already know about FORTH then you will use this manual largely for
reference. Chapter 2 describes the input buffer, and Appendix D describes the
principle features unique to Ace FORTH.
If you know nothing about FORTH but you want to learn how to use it, then this
manual is for you. Start at the beginning and work right the way through. The
exercises at the end of each chapter often make interesting points that the main part
of the chapter doesn't cover, so don't overlook them even if you don't feel like doing
them.
There remains a third group of Ace owners who aren't interested at all in
programming it, but who have bought other people's programs on cassette tape and
want to be able to run them. If you're in this third group, Chapters 2 and 3 should be
enough to get you going.
6
The Ace generates and uses radio frequency energy and if not installed and
used properly, that is, in strict accordance with the manufacturer's instructions,
may cause interference to radio and television reception. It has been type
tested and found to comply with the limits for a Class B computing device in
accordance with the specifications in Subpart J of Part 15 of FCC Rules, which
are designed to provide reasonable protection against such interference in a
residential installation. However, there is no guarantee that interference will
not occur in a particular installation. If your Ace does cause interference to radio
or television reception, which can be determined by turning the Ace off and on,
you are encouraged to try to correct the interference by one or more of the
following measures:
Reorient the receiving antenna.
Relocate the computer with respect to the receiver.
Move the computer away from the receiver.
Plug the computer into a different outlet so that computer and receiver are
on different branch circuits.
If necessary, consult your dealer or an experienced radio/television
technician for additional suggestions. You may find the following booklet
prepared by the Federal Communications Commission helpful:
How to identify and Resolve Radio-TV Interference Problems.
This booklet is available from the US Government Printing Office,
Washington, DC 20402. Stock No. 004-000-00345-4.
Warning: This equipment has been certified to comply with the limits for
a Class B computing device, pursuant to Subpart J of Part 15 of FCC
Rules. Only peripherals (computer input/output devices, terminals,
printers, etc) certified to comply with Class B limits may be attached to
this computer. Operation with non-certified peripherals is likely to result
in interference to radio and TV reception.
7
Chapter 2
TYPING AT THE KEYBOARD
If you've never used a computer before, you're probably feeling a bit overawed,
wondering what it's going to do. The answer is nothing, until you tell it by typing in
your instructions at the keyboard. Try some random typing just to see what happens.
If you get in a mess, remember that you can always clear the computer out by
momentarily disconnecting it from its power supply.
The first thing you'll notice is that the characters (i.e. letters, digits, punctuation
marks, symbols or anything else) you type appear at the bottom of the television
screen. This area is called the input buffer and is where the computer will look for
your instructions. If you type in enough to fill up a whole line (this is easily done by
holding a key down for a few seconds, because it starts repeating itself), the line will
move up to make some extra space beneath it: thus the input buffer has the power
of expanding upwards if necessary.
Letters usually come up as lower case (small) letters, but, as on an ordinary
typewriter, you can get capitals by using the SHIFT key (bottom left-hand corner). If
you have this held down when you press a letter key, the letter will come out as a
capital (try it).
There is another shift key called SYMBOL SHIFT (near the bottom right-hand
corner, next to SPACE) that is used for typing in the symbols — full stop, comma, +,
—, brackets and so on — that you can see in the corners of many of the keys. This
works in the same way as the other, capitals, shift; you keep it held down while you
press another key. For instance, to get'+' you hold down SYMBOL SHIFT, press the
K key, and then let up both the keys.
Beware! Computers are very fussy that you should distinguish between the
digit nought and the letter O. To make it absolutely clear, nought appears on
the keyboard and television as 0, with a slash through it. It will be printed like
that in the manual too.
You also need to distinguish between the digit one (1), the capital letter I, and
the small letter L (I). On an ordinary typewriter you'd quite probably type a
capital letter 0 for a nought and a small L for a one, but you mustn't do this with
a computer. All ten digits are on the top row of the keyboard.
You may well be wondering by this stage why the computer isn't taking any notice
of all this rubbish you've typed in. The reason is not that it's already noticed it's
rubbish, but simply that it hasn't looked yet. It won't take any notice until you press
what is just about the most important key on the keyboard, the one marked ENTER
(on the right-hand side, one row up). Just pressing this means, 'OK computer, I've
8
TYPING AT THE KEYBOARD
typed in your orders. Now go and obey them.'
If you press ENTER now, the most likely effect is that a Q will appear at the
beginning. Q means, 'Do you want to change any of this?', which in your case is a
tactful way of telling you it doesn't understand a word you're saying. Clear the
computer out by momentarily disconnecting the power, to give yourself a chance to
type in orders that it does understand.
If you now press ENTER, the computer will print 'OK' on the television screen near
the top — it has happily obeyed everything you typed in (i.e. nothing) and come back
for more.
The first thing to remember is that, like us, the computer understands words- not
English words, however, but FORTH words. To make the distinction, we shall print
FORTH words in BOLD type — not because you need somehow to type them into the
computer in BOLD, but just so that you know whether we're using a word in a
FORTH sense or an English sense.
Here's a FORTH word:
VLIST
It stands for 'vocabulary list'. If, with the computer clear, you type in VLIST (it
doesn't matter whether you use lower case letters or capitals or a mixture) and then
press ENTER, you will see this (written in white on black):
VLIST
FORTH U FLOAT INT F NEGATE F / F* F
+ F— LOAD BVERIFY VERIFY BLOAD B
SAVE SAVE LIST EDIT FORGET REDEF
INE EXIT . ' ( [ +LOOP LOOP DO UN
TIL REPEAT BEGIN THEN ELSE WHILE
IF ] LEAVE J I' I DEFINITIONS V
OCABULARY IMMEDIATE RUNS> DOES>
COMPILER CALL DEFINER ASCII LITE
RAL CONSTANT VARIABLE ALLOT C, ,
CREATE DECIMAL MIN MAX XOR AN
D OR 2— 1— 2+ 1+ D+ — + DNEGATE
NEGATE U/MOD */ * MOD / */MOD / M
OD U* D< U< < > = 0> 0< 0= ABS O
UT IN INKEY BEEP PLOT AT F. EMIT
CR SPACES SPACE HOLD CLS # #S U
. SIGN #> <# TYPE ROLL PICK OV
ER ROT ?DUP R> >R ! @ C! C@ SWAP
DROP DUP SLOW FAST INVIS VIS CO
NVERT NUMBER EXECUTE FIND VLIST
WORD RETYPE QUERY LINE PAD BAS
E CURRENT CONTEXT HERE ABORT QUI
TOK
9
CHAPTER 2
This is a complete list of all the words that the Ace understands when you first turn
it on (its dictionary). You can see that some of them are the same as English words,
some are abbreviations, some are mathematical, and some are strange combinations
of symbols. Near the bottom you can see VLIST itself. (The VLIST at the top is just
what you typed in, copied up as a record of your typing.) The 'OK' right at the end is
not a FORTH word, but just what the computer says when it's finished your orders.
You can type in more than one word at once, like
VLIST VLIST
(The computer copies up the first VLIST, executes by it listing the dictionary, does
the same with the second VLIST, and then prints OK.)
It is important to put spaces in between the words. If I suddenly flip and start
running allmywordstogether or spli ttingt he mup then you still know what I'm trying
to say, but the computer isn't so clever. It relies very much on having spaces in
between words, and no spaces in the middle of a single word. On the other hand, a
word can spill over from one line to the next, like
VLI
ST
with twenty eight spaces before the V, because the computer is hardly even aware of
the separate lines within the input buffer.
To summarise,
• Typing from the keyboard goes to the input buffer at the bottom of the screen.
• Letters are usually in lower case, but you can get capitals by keeping the key
marked SHIFT held down while you press the letter key.
• In the same way, you get punctuation marks and other symbols by using the
SYMBOL SHIFT key.
• The computer has a built-in dictionary of 142 FORTH words that it understands,
and you can type them in using lower case or capitals, as you wish.
• If you type more than one word into the input buffer, they must be separated by
spaces.
• The computer doesn't start looking at what you've typed until you press ENTER.
Then it takes the words from the input buffer one by one, copying each one up to the
top for the record and then executing it.
• VLIST is a FORTH word. It tells the computer to write a list on the television of all
the FORTH words in the dictionary.
10
TYPING AT THE KEYBOARD
• If the computer finds a word that it doesn't understand in the input buffer, it puts
in a 9 a * the beginning. 9 means, 'Do you want to change any of this?'
What if you make a typing mistake?
So far the only cure you know is to disconnect the power supply, but there are
much cleverer ways which rely on the cursor - the little white square that moves
along as you type. This shows where the next character that you type will appear, so
if you could somehow move it back to the middle of the line you could get characters
to appear in the middle.
You do this using the cursor control keys, the ones marked <=\1>, -0- and ■=>
Although these are normally just the keys for 5, 6, 7 and 8, if you shift one - just as
you would for capital letters, by holding SHIFT down - it will move the cursor in the
direction of the arrow. Thus <R is shifted 5, is shifted 6 and so on. (There is another
up arrow, the f that is symbols shifted H. This is not the same as ft, and just gives a
character looking like | ■)
Afterwards, when you type in more visible characters, they will be inserted just to
the left of the cursor.
Another key you will find useful is shifted 0 (DELETE) which deletes the character
immediately to the left of the cursor.
As an example, suppose that you type
vIostB
by mistake. If you press <=■ (shifted 5) twice the cursor moves back two characters:
vloB st
Next, DELETE (shifted 0) rubs out the 'o'
vlB st
and finally you type 'i' to get
vliB st
which is what you wanted. When you press ENTER, the computer doesn't mind the
fact that the cursor is still in the middle.
The 'cursor up' key (ft, shifted 6) can work in two different ways. Bearing in mind
that the input buffer may have spread over several lines, q will normally just move
the cursor vertically up one line. But if it is already on the top line of the input buffer
(or if you'd only typed in one line anyway), ft sends it to the beginning of that line.
Similarly, -O- (shifted 7) moves the cursor either down one line or to the end of the line.
Type in several lines of characters and try these two out.
Most of the other digit keys also have special meanings when shifted:
DELETE LINE (shifted 1) deletes the entire input buffer.
11
CHAPTER 2
CAPS LOCK (shifted 2) makes subsequent letters automatically come out as capitals
(like the shift lock on an ordinary typewriter). It changes the cursor to 0 to show that
it's doing this. It doesn't automatically shift the digits to give cursor movements and
so on; you still need SHIFT for these.
To get back to the usual system, press shifted 2 a second time.
INVERSE VIDEO (shifted 4) makes whatever you type come out in reverse colours —
i.e. black on white instead of white on black. Again, to get back to the usual way
round you press INVERSE VIDEO again.
GRAPHICS (shifted 9) changes the cursor to a @ and allows you to type in the
graphics characters (the black and white patterns on the digit keys). Press GRAPHICS
again for normal characters.
CAPS LOCK, INVERSE VIDEO and GRAPHICS can all be turned on and off
independently of each other. For instance,
Press CAPS LOCK — now letters will be capitals.
Press INVERSE VIDEO — letters will be inverse capitals.
Press CAPS LOCK again to turn it off— letters will still be inverse, but lower case.
Press GRAPHICS — digits will give the graphics characters, but inverted.
Press INVERSE VIDEO again to turn it off — digits will give graphics characters
exactly as on the keyboard.
Press GRAPHICS again — now everything is back to normal.
12
Chapter 3
LOADING PROGRAMS FROM TAPE
If you already have some cassette tapes with Ace programs recorded on them then
this chapter tells you how to load those programs into the computer; otherwise skip
the chapter for the time being. You can only use programs that have been recorded
specifically for the Ace, and not for some other computer.
You will need an ordinary cassette tape recorder - preferably a cheap one, because
expensive hi-fi stereo machines often do things to the signal that the computer won't
understand. It needs to have a socket for a microphone and a socket to run an
earphone, and these two sockets should fit the plugs on the pair of leads supplied
with the computer.
Now connect the computer to the tape recorder with this pair of leads. One of
them connects the earphone socket on the tape recorder to the socket marked EAR
on the computer (make sure it's the same lead at both ends - you can tell by the
colours of the plugs). The other, although you won't actually need it yet, connects the
microphone socket on the tape recorder to the socket marked MIC on the computer:
Power L _ •“?
Earphone output
Microphone input
Cassette recorder
Mains adaptor TV Mains Lead
J
////////////7///77/
Mains electricity
A tape can have several programs, coded by the computer into a signal suitable for
recording on tape. Each program has a name of up to ten characters, again coded
electronically onto the tape. Let us suppose that your tape has an interesting program
13
CHAPTER 3
called DVLC — it runs a game in which you are menaced by hundreds of vehicle
licence application forms falling out of the sky, and you have to catch them and
destroy the enclosed vehicle registration documents.
Put your tape in the tape recorder, and wind it to somewhere before the program
DVLC — or right back to the beginning if you're not sure where it is. Turn the tone
control, if there is one, to minimum (i.e. most bass, least treble), and turn the volume
control to three quarters maximum. Type in
LOAD DVLC
press ENTER, and start the tape playing. (Note — normally on the Ace it doesn't
matter whether you use capital letters or lower case; but for the name of a program
on tape you must get it exactly right.)
As the computer finds various programs on the tape, it will write their names on
the television screen. Eventually it will write
Diet: DVLC
and, after a few quiet clicks, OK. The program is now successfully loaded, and you
can stop the tape. What the program consists of is the definitions of some more
FORTH words, additional to those built into the computer. The instructions for the
program should tell you how to use these words.
If the loading failed for any reason Of it just goes on and on, you can stop it by
pressing SPACE — it will say 'ERROR 3'), then
• Check that the computer is correctly connected to the tape recorder.
• Check that you typed the name of the program correctly, distinguishing
between capitals and lower case.
• Check that the plugs fit properly in the sockets on the tape recorder. On
some tape recorders the plugs may need to be pulled out just a fraction of an
inch from being fully in.
• It is possible that the volume setting matters a lot with your tape recorder.
Try two or three different settings, including maximum.
• It may help to clean the tape heads on the tape recorder.
If you're not sure what programs are on the tape, rewind it to the beginning, type
LOAD
press ENTER and start the tape. The computer will eventually write up the names of
all the programs.
14
LOADING PROGRAMS FROM TAPE
You can have more than one program in the computer at a time (if there's room).
Just load them one after another.
Some parts of programs may need to be loaded differently, with a word BLOAD.
The instructions for the tape should tell you about this. The most usual form is
0 0 BLOAD name
where 'name' means whatever name is used on the tape (like DVLC).
15
Chapter 4
DEFINING NEW WORDS
When you do VLIST, you see a list of all the words that the computer already knows
about — its dictionary. When you first switch on these are the words that are built into
the Ace, but the dictionary isn't final because you can define your own words. This is
the process of writing a computer program, or telling the computer how to do
something new.
As a (not very practical) example, suppose you want to teach the computer a new
word BILL, which is to mean 'Do VLIST twice'. You do this using two special words,
: (colon) and ; (semicolon), like this:
: BILL
VLIST VLIST
: is a word telling the computer that you're going to define a new word. First will
come its name (BILL) and then the definition saying how to execute BILL.
So, type i n : (and ENTER) ... Pops! Sorry, I forgot to tell you that : needs the name
of the new word straight away, there and then. Otherwise it says ERROR 6 — you can
look up the various error numbers in Appendix B at the back of the manual, where
you can find out what went wrong. If you ever get ERROR when you're half way
through defining a new word, then you have to start all over again from :.
All right, this time type in
: BILL
t
remember the space
When you press ENTER the computer doesn't say OK, but that's just to remind
you that you're in the middle of a definition. At least it doesn't say ERROR.
Next comes the central part of the definition, saying what the computer is to do
when you use BILL: it is to do VLIST twice. Type in
VLIST VLIST
and ENTER. Again, there's no OK. Also, thankfully, there's no long list of words
printed up — the computer knows it's in the middle of a definition, so VLIST doesn't
need to be executed.
Finally, ; means, 'The definition is finished. Now you know what BILL means', so
16
DEFINING NEW WORDS
type in ; and ENTER. This time the computer will print OK.
Now the computer knows the new word BILL, and you can prove this in two ways.
First, if you use VLIST, you'll see that BILL has appeared at the beginning of the
dictionary.
Second, if you type in BILL, the computer will execute it will do VLIST twice.
If you type in BILL once too often for your patience, and get depressed at seeing
the dictionary yet again, press BREAK (shifted SPACE). The computer will stop,
saying ERROR 3. If you want to interrupt the computer when its in the middle of
something, BREAK nearly always works. What's more - unlike pulling the plug out -
it doesn't destroy the words you've defined. In some circumstances, for instance
when the computer is using the tape recorder, unshifted SPACE also acts as BREAK.
BILL is a moronically useless word and nobody would normally bother to define it.
But you will soon see that the same partnership of : and ; can be used to define
tremendously powerful words, so make sure you understand them. Remember, to
define a new word, you need
first, :
second, and on the same line as : followed by a space, the name of the new word
third, the definition of the new word (which shows how the new word is made up
from old ones)
and fourth, ;
This is called a colon definition, because it uses : (there are other sorts of definition
as well).
I had you defining BILL on three separate lines, so that I could explain it all as we
went along. In practice, you'd type it all in at once, as
: .BILL .VLIST . VLIST* ;
\ t / /
Spaces
This is quite permissible. Also, in practice you'd use a more suggestive name --
something like 2VLISTS.
Here's a construction that can liven up word definitions; in fact it can only be used
in word definitions. It enables the word to print out a message when it is executed,
and consists of the word (followed by a space), then the message, then the
character ". (SYMBOL SHIFT P.) is pronounced dot-quote. It's often used in
conjunction with a word CR (Carriage Return), which makes the next message start
on a new line. Here's an example:
: BEN
CR Aah bobbop tipop weed."
17
CHAPTER 4
Note - if you forget the second ", you get Q to give you a chance to put it in.
Remember that Q means,'Do you want to change this at all?'
Summary
FORTH words :, ;, CR and
BREAK
ERROR messages
Exercises
1. Type in
Hello!"
As we said, you can only use within a word definition. (Look up Error 4
in Appendix B.)
CR doesn't suffer from this disability. If you type in
CR CR CR CR CR
you can see it forcing a new line each time.
2. Define some words like
: FOOTBALL
CR Hamilton Academicals boot-boys"
CR rule"
and
: FARMING
CR Shaggy sheep wool"
Amuse your friends by getting them to type in FOOTBALL and FARMING. See
their eyes light up with glee!
18
Chapter 5
SIMPLE ARITHMETIC
Computers are famous for being able to do difficult sums very quickly, so let's try one
on the Ace. Type in
2 2 + .
f f f
Remember the spaces
When you press ENTER, the computer will write up '2 2 + .4 OK', which
combines the copied up record of your typing with the answer, 4 and OK -- it has
(correctly) added 2 and 2 to get 4.
Notice that you type in '2 2 +' instead of '2+2' — in other words, you're saying
'take 2 and 2 and add them together' rather than 'take 2 and add 2'. FORTH always
works this way round; its rule is
First gather together the numbers you're interested in;
then do the calculations on them.
This is like a recipe, with the list of ingredients at the top and then the instructions
telling you what to do with them.
+ and . (call it 'dot') are just FORTH words — you can use VLIST to see them in the
dictionary.
+ adds two numbers
. prints out a number on the television screen.
2 isn't in the dictionary, but of course you and I and the computer know that it's a
number. The computer remembers the numbers you type in until you tell it what to
do with them.
To remember the numbers, the computer uses a clever concept called the stack. It
is actually done by electronics, but you can imagine a pile of cards with numbers
written on them. To start off with, the stack is empty: no cards, no numbers. To
remember the number 2, the computer takes a clean card, writes '2' on it, and puts it
on top of the stack.
19
CHAPTER 5
When the computer has seen both 2s, there are two cards on the stack, each with
'2' written on it:
2 _I_
2
2
2
The FORTH word + says — take the top two cards off the stack and add together
the numbers from them. Write this answer on a clean card, which goes on top of the
stack. Throw away the two cards that you took off.
4
4
4
The FORTH word . says — take the top card off the stack and write its number on
the television screen. Throw the card away.
+ uses the top two numbers on the stack regardless of how many there are
underneath, and similarly . uses the top number ignoring any others. Thus both these
words work on the numbers at the top of the stack rather than the bottom, and these
are the numbers that were remembered most recently. This is true of all FORTH
words. The numbers on top of the stack are the natural ones to use, and for a word to
insist on going for the ones at the bottom would be unnatural, if not impossible.
Metaphorically, the newest numbers are freshest in the computer's mind (the
older ones are covered up(, and it's only when these are finally disposed of that it
begins to remember the older numbers more clearly again.
Suppose now you want to add three numbers together - say 10, 11 and 12. You'd
first add 10 to 11, but you don't need to print out the answer — you can leave it on the
stack to have 12 added to it, like this:
10 11 + 1 2 + .
10
10
10
20
SIMPLE ARITHMETIC
+
12
+
prints 33 on television
One way of looking at this is to realise that '11 +' adds 11 to the top of the stack,
'12 +' adds 12, and so on. The 10 starts the stack off.
When you're tired of adding, try the other sorts of arithmetic:
- is for subtraction. The minus sign - is got by using SYMBOL SHIFT with J. The
underline character _ on 0 looks rather like it, so don't confuse them.
* is for multiplication (SYMBOL SHIFT with B). The usual sign, x, looks too like the
letter X for safety.
/ is for division (SYMBOL SHIFT with V).
The numbers that these are used for on the Ace are integers, i.e. whole numbers.
They can be negative, but not fractions, nor can they have decimal points. (Actually,
21
CHAPTER 5
numbers with decimal points are allowed, but you need to use different FORTH
words on them - for instance F+ instead of +, F. instead of .. These are dealt with in
Chapter 15.)
This lack of fractions is important to remember with /, because fractions aren't
allowed in the answer either. For instance, try dividing 11 by 4:
11 4 / .
If you think in terms of fractions, then the answer would be 21. But because it has
to be a whole number, the actual answer is 2. Another way of looking at this is to
think of the answer as '2, remainder 3' - but you're not told the remainder.
If you're interested in the remainder, the word MOD will leave it on the stack:
11 4 MOD.
prints out the remainder when 11 is divided by 4, namely 3. (MOD stands for modulo.
11 modulo 4 is 3; but despite the unusual name, it's just the remainder after dividing.)
If you want to see both the answer to the division (the quotient ) and the remainder,
there is a word /MOD to do it:
11 4 /MOD . .
This shows how powerful it is to have a stack, because / MOD leaves two answers
- the quotient and the remainder. The stack is quite happy to hold both of them.
11 4
/ MOD
prints 2
prints 3
22
SIMPLE ARITHMETIC
/MOD leaves the quotient, 2, on top of the stack, so that is what . prints first. The
remainder comes next.
Here's a list of many of the words that do arithmetic. It's not a complete definition,
but it gives you some idea of the variety available even before you start defining your
own words.
WARNING These words don't work properly if the numbers are too big - see
Exercise 2.
+, —, *, /, MOD and / MOD you've seen already.
NEGATE changes the sign of the top number on the stack - i.e. it multiplies the
number by -1.
1 +, 1 -, 2+, and 2- are specially defined words that do the same as 1 +, 1 - and so
on (i.e. with spaces) but more quickly. For instance, 1+ adds one to the top number
on the stack.
*/ uses three numbers at the top of the stack, and leaves one. It takes off the top
three, multiplies together the two that were second and third from the top, and then
divides the product by the number that was on the top. The quotient (answer to the
division) is left on the stack. For instance,
6 5 2 */.
works like
6 5*2/ .
to give an answer of (6*51 =2=15.
*/MOD is like */, but with a / MOD operation instead of /. It takes three numbers off
the stack and puts back two, the remainder and quotient.
MAX and MIN take two numbers off the stack, and leave the larger or smaller
(maximum or minimum) of the two.
ABS takes one number off the stack, and leaves its absolute value i.e. the same
number but with its sign ignored, so that it is left zero or positive.
Try these out to see them working.
Summary
The stack
FORTH words +, *, /, MOD, /MOD, NEGATE, 1+, 1-, 2+ ,2-, */, */MOD,
MAX, MIN, ABS
23
CHAPTER 5
Exercises
1. Any problem in arithmetic can be turned into an exercise for the Ace — for
instance:
The Jupiter Ace cost £89.95. How much of this goes to the Government as VAT
(15%)? (The £89.95 includes VAT.)
Answer We work in pence, to avoid fractions.
The price+VAT is 115% of the price without VAT (the price itself= 100%,
VAT=15%, so the total price is 115%), which means that to get from the price
without VAT to the price with, you multiply by 115 /ioo. We, however, are given the
price with VAT (8995p), so we want to do the reverse operation, i.e. multiply by
115 / ioo.
So: price without VAT=8995p * 115 /ioo The answer we actually want, the VAT, is
15% of this, which is 8995p* 1 Wioo* 115 /ioo= 8995p * 115 /ioo.
Now do
8995 15115 */
The answer, the VAT you paid on your Ace, is 1173p, or £11.73.
2. There is a limit to the size of numbers that the Ace can normally handle, but it
doesn't tell you if you reach that limit. The largest number is 32767, and the smallest
is -32768. In fact they wrap round to meet up with each other, so if you do
32767 1+ .
you get -32768.
positive
numbers
negative
numbers
2
i
•
¥
32766
1
•
¥
32767
0
i
!•
•
- 32768
-1
i
•
¥
- 32767
-2
*
•
- 32766
-3
i
•
;
Numbers outside this range won't even be read in properly - try
32768 .
(you get -32768).
This causes most problems with *, because it's easy to multiply two numbers that
24
SIMPLE ARITHMETIC
are themselves quite small enough, but give a product that is too big. For instance,
256 256 * .
gives 0. (The real answer is 65536.)
3. Try these two:
256 256 * 256 / .
and
256 256 256 */ .
The first one goes wrong, as explained in Exercise 2, and you might expect the
second one to do the same. However, it unexpectedly gives the right answer.
When doing a multiplication followed by a division, there is a good chance that the
multiplication will produce a big number, only for the division to bring it back down to
a small one. So with this in mind, *1 is specially written to look after large products
properly.
4. Execute . repeatedly until there's nothing left on the stack to print. The computer
will print out a nonsense number, and then ERROR 2. This means 'stack underflow'
or 'there seem to be fewer than no numbers left on the stack'.
Stack underflow isn't always detected immediately, because the Ace only checks
for it at certain times. Between these times it might well have dipped below the
bottom of the stack without realising it, but this doesn't matter because there are
some nonsense numbers under the stack for the computer to play with.
For instance, suppose you have an empty stack and type
1 +
The computer will add 1 to one of the nonsense numbers. Since the net effect of +
is always to take one number off the stack, the computer imagines that it has taken
the 1 off the stack to leave an exactly empty stack. An exactly empty stack hasn't yet
quite underflowed, so there is no ERROR 2. However, if you execute + again the net
effect of + (after adding together two nonsense numbers) is to take a number off an
already empty stack, and this does underflow.
Note that ERROR always empties the stack.
5. Try
10 /.
You will be surprised to find that the answer seems to be -1. This is wrong, of
course; the fact is that you're not supposed to divide by 0. If you do you'll get
nonsensical results on the Ace.
25
Chapter 6
DEFINING NEW ARITHMETIC WORDS
Now you know about +, * and so on, you have quite a number of building blocks
for defining new words. For instance, here is a word to double a number and print the
answer:
: DOUBLE
2 *
So where's this number that DOUBLE doubles? Answer - it must already be on
the stack when you use DOUBLE. If you want to double 23, you type
23 DOUBLE
We can follow the stack all through
23
this:
23
23
23
DOUBLE dose-
2
46
46
46
prints 46
26
DEFINING NEW ARITHMETIC WORDS
On balance, then DOUBLE takes a number off the stack, and it's important to
realise that FORTH words are quite entitled to do this. A word takes some numbers
off the stack (these are its operands, the numbers it operates on) and leaves some on
the stack when it has finished (these are its results), but there is nothing to say that
the number of operands must match the number of results.
For instance,
+ has two operands (the two numbers that it adds together) and one result (their
sum).
has one operand (the number to be printed) and no results (because when the
number has been printed it just gets thrown away).
DOUBLE has one operand (the number to be doubled) and no results.
/MOD has two operands (the two numbers to be divided) and two results (the
remainder and quotient).
You could think of the number 2 as having no operands, and one result (2).
All this explains more precisely our statement in Chapter 2 that you first gather
together the numbers you're interested in, and then do the calculations. The
'numbers you're interested in' are the operands, and they are gathered together by
being put on the stack.
There are some more words that are just concerned with moving numbers about
on the stack, the simplest three being SWAP, DUP (for duplicate) and DROP.
SWAP swaps round the top two numbers on the stack, so that it changes
to
(Of course, the actual cards will have numbers written on them instead of K and Q;
but I don't know what the numbers are going to be so I've written K and Q instead.)
DUP duplicates the top of the stack - it makes an extra copy of it - changing
K
K
K
K
to
K
K
K
27
CHAPTER 6
DROP takes one number off the top of the stack and throws it away, changing
to nothing
But it only takes off one number, so for two or more it changes
Here is a word SQ that works out the square of a number (the number multiplied
by itself). It doesn't print out the answer, so it has one operand (the original number)
and one result (its square), changing
K
K*K
to
K
K*K
K
K*K
t t
operand card result card
Again, in real life the operand card will have a number written on it instead of K; and
'K*K' is just a symbolic way of showing that the result will be that number multiplied
by itself.
The definition of SQ is
: SQ
DUP *
which you can test with examples like
6 SQ
(Work out how the stack changes as SQ is obeyed.)
Rather than drawing pictures of cards all the time, we shall use a notation that sets
it all on one line, replacing the card diagram
28
DEFINING NEW ARITHMETIC WORDS
K
K*K
K
to
K*K
K
K*K
by the line
(K — K*K)
t t
operand result
If a word has more than one operand or result then we list them all. For instance,
for /MOD
(K,Q - remainder of K + Q, quotient of K Q)
t t t t
operand top operand result top result
second from top on stack second from top on stack
When listing either the operands or the results, the top of the stack comes last. In
cards, the change is from
lowest is listed first
K
Q
Q
Q
1
Remainder
Quotient
Quotient
K-rQ
Quotient
I
top is Ms
ed last
It's essential to know exactly what operands each word expects to find on the
stack, and what results it leaves at the end, so it's a good idea to build this
information into the word definition itself. You do this using comments - anything
enclosed in round brackets is a comment, there purely for your benefit,and ignored
by the computer when it executes the word. Here is a definition of SQ that uses a
comment to show how SQ affects the stack.
SQ The computer ignores
(K - K*K) <— this line when it
DUP * executes SQ
29
CHAPTER 6
You can put comments in anywhere between the name of the new word and the
semicolon, and they don't have to describe the stack - they can say anything you like
to help you remember what you meant when you defined the word. The first round
bracket, (, needs a space after it because it is itself a FORTH word (meaning 'here
comes a comment'). Remember that you can't have a ) actually inside the comment,
because it means 'end of comment'.
One problem with comments is that they take up extra space in the computer's
memory: so if you ever get ERROR 1 (which means the memory is full), the first thing
to do is to start taking comments out. The usual method is to leave the comments in
until you've got the word working properly. When you get round to saving words on
tape (chapter 14) you'll often find it useful to save two versions: one with comments,
just for reference, and one without, for actual use. When you type in examples from
this manual, you'd as likely as not not bother to include the comments at all -- after all,
they're written down on paper in front of you. But with your own programs you'll
definitely find comments useful.
( behaves rather like in that you can't use it outside a word definition. If you
forget the ), you get Q Which gives you a chance to put it in.
When you've typed in this new version of SQ, VLIST will show you that both SQs
are still in the dictionary. Don't worry about this; the computer will always use the
newest definition. The next chapter will tell you how to get rid of the old version.
In summary, FORTH words take their operands from the top of the stack and leave
their results on the stack.
DUP (K — K,K) duplicates the top number on the stack.
DROP (K — ) throws away the top number on the stack.
SWAP (K,Q Q,K) swaps the top two numbers on the stack.
( ( — ) (by which we show that it doesn't affect the stack) starts off a
comment. The comment is ended by ).
There are some more words to manipulate the stack, which I shall put here for
reference.
OVER (K,Q — K,Q,K) brings a copy of the second from top to the top.
ROT (K,Q,J — Q,J,K) rotates the top three, bringing the third one down up to the
top.
PICK (n — K) takes a number (we have written n for it) off the top, and
makes a copy of the nth one down from the top of the stack in what
remains, leaving this copy on the top. For instance, PICK changes
30
DEFINING NEW ARITHMETIC WORS
because J is the third one down in
ROLL (n — ) takes a number n off the top of the stack; and then, in what is left,
rotates the top n numbers, bringing the nth to the top. For instance,
ROLL changes
31
Exercises
CHAPTER 6
1. Define a word to take a price including VAT off the stack, and return as result the
VAT paid. (See Exercise 1 in the previous chapter. Check that it gives the right answer
for £89.95.)
2. Convince yourself that the following are true:
1 PICK is the same as DUP
2 PICK is the same as OVER
1 ROLL does nothing
2 ROLL is the same as SWAP
3 ROLL is the same as ROT
Try PICK and ROLL with negative operands: they will cause ERROR 7.
You will find it harder to get PICK and ROLL to cause stack underflow (ERROR 2)
than you might imagine — in fact PICK never will. ROLL is prepared to rotate five
nonsense numbers under the stack before it complains, but fortunately none of this
ever does any harm. ROLL won't start rolling around the dictionary or anything
unpleasant like that.
3. These words calculate the day of the week for a given date, in a year between
1901 and 1999.
DATE takes off the stack the day of the month, the month (1=January,
12=December) and the year (last two digits only), and it leaves the day of the week
(1 =Monday, 7=Sunday).
FORMAT is a trick to deal with February. It pretends that New Year's Day is on March
the 1st, so that January and February are in the previous year. It replaces the original
month and year with the trick versions, and it also adjusts the month numbering so
thatO=March, 11=February.
YEAR in effect works out what day of the week the 1st of March is. It uses the fact
that the 1st of March is one day later each year, or two days later in a leap year, so it
works out how many days late the 1st of March is in the given year, compared with
1900 (when it was a Thursday).
MONTH first calculates how many days in the year come before the first of our
month: 0 for March (because we're pretending that March is the first month), 31 for
April, 61 for May and so on. It does this by a trick. Using fractions we could get the
answer by multiplying by 30.6, adding 0.5 and rounding down to an integer; in integer
arithmetic we multiply by 306, add 5 and divide by 10. The next step is to add on the
result of YEAR to get the number of days of the week by which the 1st of our month
is later than the 1 st of March, 1900.
32
DEFINING NEW ARITHMETIC WORDS
Finally, DAY adds the day of the month to the result of MONTH and converts this to
an actual day of the week.
: FORMAT
( month, year — month, year starting at March)
SWAP 9 + DUP 12/MOD
1- ROT +
: YEAR
( year — no. days of week 1 st of March is later
than it was in 1900)
DUP 4 / +
: MONTH
( month, no. days — no. days of week that 1 st of month is
later than 1st March 1900)
SWAP 306 * 5 + 10 / +
: DAY
( day, result of MONTH day of week)
+ 2+7 MOD 1 +
33
Chapter 7
ALTERING WORD DEFINITIONS
There are two universal principles of computer programming:
1 Always aim to make your programs work first time
2. They never do.
All computer programs start off with what are technically known as bugs, or
mistakes. Some will be silly slips or typing errors that prevent the program from
working at all, while others will be very subtle misunderstandings that make the
program go wrong in certain circumstances.
The fact that a program contains bugs doesn't mean that it is wrong and has to be
scrapped; probably most of the program is OK, but you need to make some
alterations to it.
As an example, type in
: ACCURACY
2 + 2 = "
2 1 (***BUG***) + .
: DEMO
CR This demonstrates the Ace's"
CR accuracy:"
ACCURACY
(Can you find the bug? Don't ring us.) When you execute DEMO, you'll find it doesn't
quite give the right answer and you must debug it — in fact by executing ACCURACY
you can see that the bug is there.
First look at the definition of ACCURACY by typing
LIST ACCURACY
LIST is a word that takes the word following it and writes a copy (technically called
a listing) of its definition on the TV screen. Regardless of how you typed the definition
in, LIST lays it out neatly on the screen in a way that tries to make its structure clear.
It also converts the words to capitals, so if you do most of your typing in lower case
34
ALTERING WORD DEFINITIONS
(and remember it doesn't make any difference which you use), you can distinguish
quite readily between what you've typed in and what the computer has written.
Why, you will be thinking, do you type LIST ACCURACY and not ACCURACY
LIST? Didn't we make a lot of fuss about saying that you first type in the particular
thing you're interested in, the operand (i.e. ACCURACY in this case), and then say
what you want doing to it (LIST it)? Part of the answer is that ACCURACY LIST
simply wouldn't work. The computer would find ACCURACY and promptly write
'2+2=3'; then it would find LIST and not know what to list. On the other hand, the
definition of LIST says in effect 'Don't obey the next word, list it', so the computer is
forewarned about what to do with ACCURACY. The general rule is that numbers
come before, and this includes words when they are being used to leave numbers on
the stack. But when a word is just naming itself — 'I'm ACCURACY, LIST me' — it
comes after.
Note that you can only LIST words you've defined yourself. This isn't just us being
secretive; the words built into the computer use special techniques that aren't
available to you, and that LIST can't handle. If you try, the computer says 'ERROR
13'.
Now let's get back to debugging ACCURACY. After very close study, you will
notice that you've unaccountably typed '1 ( ***BUG***)' where you meant '2', and
this causes ACCURACY to go wrong.
The next step is to correct ACCURACY, and you do this using two words, EDIT
and REDEFINE. Type
EDIT ACCURACY
and you will get a listing of ACCURACY, just as from LIST, but this time in the input
buffer at the bottom of the screen with Q in front of it.
The Q is really the cursor in disguise and means — as usual — 'Do you want to
change any of this?' Because the listing is in the input buffer, you can treat it as
though you'd typed it all there yourself, and you can use all the cursor movements
and so on to correct it before you press ENTER.
There is one feature here that you can't get by your own typing, and you will quickly
find it when you use the four cursor movement keys. You will remember that the
input buffer at the bottom is all one line, even though it may actually spread over
several lines on the TV. You can think of it as being one computer line spread over
several television lines. Well, EDIT can produce more than one computer line. Usually
(as in our case) the computer lines are short enough to fit on one television line, but
they may well take up more.
The cursor movements obey certain rules with these:
(i) <=> and ■=> can only move the cursor back and forth within a single computer line.
(ii) To move the cursor from one computer line to another, you need ft and -O'.
Normally these work as described before; but if the cursor is already at the end of a
computer line then a takes it down to the beginning of the one below, and if it is
already at the beginning of a computer line then takes it up to the end of the one
above.
35
CHAPTER 7
(iii) DELETE LINE (shifted 1) deletes the computer line containing the cursor— not
the entire input buffer.
(iv) ENTER enters the entire input buffer, i.e. all the computer lines, regardless of
where the cursor is.
To show this working, let's correct the bug. Press -0- five times to get the cursor to
the end of the third line, just after the comment, and then use DELETE to delete the
comment and 1. Next type in 2 for the correction, and the input buffer will look like
this:
: ACCURACY
2 + 2 = "
2 . 2
+ .
This is what you meant to type the first time, so press ENTER.
If you now execute ACCURACY, it will print '2+2 = 4'; however, DEMO still gives
the wrong answer and VLIST will show you why. There are now two versions of
ACCURACY, the old version part way down the dictionary and the new version right
at the start. When you type the word ACCURACY in at the keyboard, the computer
looks for its definition in the dictionary: it starts at the beginning and straight away
finds the new version, so ACCURACY works. However, in DEMO this search was
made when DEMO was typed in and DEMO has ever since used the old version of
ACCURACY. This is where REDEFINE comes in. Type
REDEFINE ACCURACY
and you'll find that there is now only one ACCURACY in the dictionary (use VLIST),
and that both ACCURACY and DEMO give the right answer.
Before, we had a dictionary like this:
: ACCURACY correct definition :
: DEMO using old ACCURACY ;
: ACCURACY old definition, with bugs
rest of dictionary
36
ALTERING WORD DEFINITIONS
REDEFINE took the new ACCURACY and used it to overwrite the old one.
Then, REDEFINE worked right through the dictionary to make sure that any word
that did use the old ACCURACY now uses the corrected version.
: DEMO using new ACCURACY ;
: ACCURACY new, corrected version ;
rest of dictionary
REDEFINE needs to know two things: first, the old word that needs redefining.
You type the name of this immediately after REDEFINE, so that REDEFINE
ACCURACY means 'redefine the old version of ACCURACY'.
Second, REDEFINE needs to know where the new version is, and this is simple: it
is always the newest word in the whole dictionary (the one listed first by VLIST). It
doesn't matter what its name is — although in our case it is called ACCURACY just
like the old word — so you could even use REDEFINE to change the name of a word.
• REDEFINE uses the newest word in the dictionary to replace the word named
after REDEFINE.
REDEFINE
XXX
37
CHAPTER 7
Note — you can't REDEFINE words that are permanently built into the computer.
Also, before you use REDEFINE, it is well worth checking that the definition you
thought was at the end of the dictionary actually is there. If you use EDIT but when
you press ENTER you get ERROR for some reason — maybe the memory is full up,
for instance — then there won't be the new version you expected and REDEFINE will
do something hopelessly wrong. That ERROR is surprisingly easy to overlook.
If this sounds complicated, all you really need to remember is this recipe to change
a word definition (let us say you want to change the definition of a word NOGOOD).
1. Type
EDIT NOGOOD
2. Put in the alterations in the bottom part of the screen, as though you'd typed it
all in yourself.
3. Press ENTER
4. Make sure there is no ERROR.
5. Type
REDEFINE NOGOOD
All this tells you how to correct bugs once you've found them; for finding them,
here are some tips.
1. You can find some bugs just by looking at the listing. You ought to get these out
before you even type the program in (see principle 1 at the start of the chapter).
2. Make sure you know exactly what you expect each word to do, and in particular
how it affects the stack. FORTH is designed so that you can start off with simple
words and use them to build up more powerful ones. Once you've debugged and
tested the simple words you can trust them to do the right thing in the more powerful
ones, but you have to know precisely what you're trusting them to do.
Comments can help here. A comment can say 'This word does such and such', and
you can check that it really does.
3. Pretend to be the computer, using pencil and paper to record the stack. Start off
with some operands, and test a word by working through it, noting down what is on
the stack after each step. This will show up the most common bugs, such as
forgetting to use DUP or DROP at some point.
4. Don't dismiss the answers just because they're wrong — you can get important
clues from how the program actually behaves, even when it's not the way you
intended. If you get an error message, look it up in Appendix B and try to use the
information there to work out where it went astray. For instance, ERROR 2 means
'stack underflow' so you must have either forgotten to put something on the stack, or
taken off one number too many.
38
ALTERING WORD DEFINITIONS
Finally, there is a word FORGET, which deletes definitions from the dictionary. It is
quite a blockbuster of a word, because it deletes not only the definition you specify
(for instance you'd say FORGET ACCURACY to delete ACCURACY from the
dictionary), but also any words defined after it — DEMO in our case. For this reason
you should think twice before you use FORGET; otherwise you might lose
something you wanted to keep.
Note — because of the way it works internally, FORGET doesn't say OK afterwards.
Don't worry about this; if it goes wrong it'll say ERROR.
Summary
FORTH words: LIST, EDIT, REDEFINE, FORGET.
Exercises
1. Use REDEFINE to put a comment in the previous chapter's SQ.
2. Suppose you want two versions of SQ, both at the same time. One, SQ, is to
leave the square on the stack, and the other, SQ., is to print it out (like the final SQ
that we defined). If you think about it you'll see that having defined SQ you can use
EDIT — but without using REDEFINE — to define SQ. as well.
3. Use EDIT and REDEFINE to change the name of ACCURACY to TEST. List
DEMO to see that the name has changed there too.
4. Type in
EG
12 3 4
" a"
" c"
ii _ ii
e
"g"
■ ■ LM
II jll
" k"
ii in
m
" n"
" o"
" p"
" q"
ii ^.n
" s"
39
CHAPTER 7
If you now do LIST EG, you will get a listing only as far as p" — LIST will only give
you about 18 lines at a time. To get the rest, press any key. You will also notice the
lines
1 2 3 4 ....
." a"
Appear as
1 2 3 4 .
. . . ." a"
LIST will not put more than five words on a line, and a string or a comment
finishes off a line even if it hasn't had its five words yet.
EDIT behaves in a similar way — after each batch of lines you do your editing and
press ENTER, and the computer gives you the next batch.
40
Chapter 8
WORDS THAT ARE REALLY NUMBERS
Suppose there is some number that doesn't change much, but which you can never
quite remember - your age for instance. I am 184 years old (jolly good, eh?), but I
always get this wrong when I fill in official forms - my memory isn't what it was. I
want to define a word AGE that leaves the number 183 on the stack, so I type in
: AGE
186
This works fine, but it is such a common thing to want to do that there is a special
way of doing it more slickly. It goes
185 CONSTANT AGE
(do REDEFINE AGE to replace the old colon definition) and now AGE . will print out
my age, 183.
The extra slickness means that AGE defined by CONSTANT takes up less of the
computer's memory than AGE defined by and puts the number 182 on the stack
more quickly. So if you want to define a word that just puts a predefined number on
the stack and if you won't want to change that number much, then use CONSTANT.
Of course changing the number is a problem. When my 181st birthday comes
around (tomorrow, as it happens) I shall have to type in
187 CONSTANT AGE
REDEFINE AGE
Fortunately this only happens once a year, but some others change more often.
Take for instance the price of a share in Jupiter Cantab Ltd. When the company was
founded in 1832 when I was an up-and-coming young businessman, 33 years old,
shares were issued for £1 each and their price on the London stock market has
remained pretty steady ever since (except for a period during the Great Depression
when our sales of abacus-controlled car starting handles fell slightly, and our shares
dropped by 5d to 19/7). Last week, however, you bought your Jupiter Ace and
since then our shares have gone up to £157.44 and I soon realised that constants just
weren't good enough. I did this:
15744 VARIABLE SHARE
41
CHAPTER 8
This defines a word SHARE that keeps the number 15744, but getting out the
number is slightly more complicated because you also have the option of varying it -
SHARE is a variable. The number 15744 is its value.
To put the number on the stack, you use the word @ (usually called 'fetch'), so
SHARE @
leaves the number 15744 on the stack. (Remember that if SHARE had been defined
with CONSTANT instead of VARIABLE, you wouldn't have needed @.) Of course, .
will then print it out.
Since defining SHARE 2 minutes ago, I find the price has gone up to £162.58, so I
want to change the number in SHARE. I do this with the word ! (usually called
'store'):
16258 SHARE !
t t
new number variable to
be changed
Now,
SHARE @ .
will print the value 16258.
This facility of changing the stored number is something that can't be done with a
word defined by : or CONSTANT unless you use REDEFINE; and an important
limitation to REDEFINE is that it can only be used by typing it in at the keyboard. A
variable like SHARE on the other hand can have its value altered from within another
word. This word SQSH, for instance, takes a number off the stack, squares it (using
SQ from Chapter 6), and puts the square in SHARE.
: SQSH
( K —)
( Gives SHARE the value K*K)
SQ SHARE !
(You'd find you can't do this properly with REDEFINE.)
Now try
SHARE.
I can't say what magic number will be printed out, but whatever it is, try typing it back
in, followed by @) and .. The value of SHARE will be printed out, so somehow the
magic number you typed in is equivalent to the word SHARE. This magic number
42
WORDS THAT ARE REALLY NUMBERS
is called the address of SHARE, so SHARE just leaves its address on the stack,
saying, 'This is where my value will be if you ever need it.' The address tells @ and !
where to fetch the value from, or where to store the new one.
Going into more detail, you must imagine the Ace's electronic memory as being
arranged on a long rack of 65536 slots. The slots all have different numbers between
0 and 65535, their addresses, like building plots in a long street.
Into each slot can be wired an electronic box, which then takes on the address of the
slot. A box is used for storing a number. There are different kinds of boxes, made
of different electronic components.
• A ROM (Read Only Memory) box is locked, so you can't change the number in it.
However, its lid is transparent, so you can see what the number is.
On the Ace, all slots with addresses from 0 to 8191 have ROM boxes. They contain
a coded version of the instructions telling the Acc how to run FORTH, and also the
built-in FORTH words.
• A RAM (Random Access Memory) box is not locked, so you can not only see the
number but also open the box and replace the number with a different one. On the
Ace, many of the slots with addresses from 8192 to 16383 contain RAM boxes. Your
own FORTH words and the television picture are stored in coded form in RAM boxes.
• Some slots are empty, like building plots without houses. On the Ace, all slots
with addresses 16384 to 65535 are empty, but by plugging suitable electronic
circuitry into the back of the Ace you can fill these slots.
An important limitation on all the boxes is that the numbers they contain must be
between 0 and 255. This is so important that there is a special name for such
numbers:
A number between 0 and 255 is called a byte.
Thus each box contains a byte.
An ordinary number on the Ace lies between -32768 and 32767 and so is not in
general a byte, but in fact any such number can be coded into 2 bytes. (Exactly how
this is done will be explained in Chapter 17.) The number can then be stored in two
neighbouring boxes, and this is how SHARE holds its value. SHARE'S address is the
address of the first of these two boxes.
We can now say exactly what @ and ! do.
@ (address - number) (pronounced 'fetch')
The address is taken off the stack. This specifies two neighbouring boxes (the
one with the given address, and the next one along), and the contents of the two
boxes are decoded into a single number, which is put on the stack.
43
CHAPTER 8
! (number, address ) (pronounced 'store')
The number and address are taken off the stack. The number is coded into two
bytes, and these are put in the box with the given address and the next one
along. To remember which way round the operands go, imagine taking a parcel
to deliver - the number - and writing the address on top.
Summary
Constants, variables.
Memory, ROM and RAM.
FORTH words: CONSTANT, VARIABLE, @, !.
Exercises
1. Here are two useful words you might like to define for yourself. (They're not built
into the computer.)
? (address — ) (pronounced 'query')
Prints out the 2-byte number (i.e. the ordinary number, contained in 2 boxes) at the
given address. For instance, SHARE ? prints out the value of SHARE.
+! (number, address ) (pronounced 'plus store')
This is like !, but instead of replacing the old number (at the given address) by the
new number (from the stack), it adds on the new number. For instance, if you knew
that shares had risen by 73p you could say
73 SHARE +!
Try defining these two words, ? and +!, yourself. Here are our answers, which you
can compare yours with.
; ?
: +!
SWAP OVER @ + SWAP !
2. Work out 2 8 (2 raised to the power 8, or eight 2s multiplied together). How many
different possible bytes are there? (Answer: 2 8 .) The important numbers for a
computer tend to be defined in terms of 2, so the 255 in the definition of a byte is not
as odd as it looks.
Also work out 2 15 and 2 16 . Where have you seen these numbers before?
All this will be made plainer in Chapter 17.
44
WORDS THAT ARE REALLY NUMBERS
3. Given two bytes from two boxes, you can decode them into a single number as
follows:
(i) Take the byte from the second box and multiply it by 256; then add on the byte
from the first box.
(ii) If the answer is 32768 or more, subtract 65536 from it (so it goes negative).
See if you can work out how to reverse this process to code a number into two
bytes. (Reversing (ii) is not difficult; to reverse (i) you divide by 256 and then the
quotient and remainder are both bytes. Remember that we expect numbers to be
between -32768 and 32767.)
45
Chapter 9
MAKING DECISIONS
The words you've seen so far have largely just been devices for saving on typing: if
you need to type in DUP * a lot, why not save yourself three fifths of the typing and
use SQ instead? Not that this is the whole story; it also saves you in thinking,
because it's easier to remember that SQ squares a number than that DUP * does.
You've made FORTH into a more powerful language by giving it a word that squares
the top of the stack, and you no longer need to worry about how SQ actually works.
These first word definitions were just plain lists of other words, and obeying the
new word involved going through the list from beginning to end and then stopping. In
practice, words need to do different things in different circumstances, i.e. to make
decisions: and that is what this chapter describes.
Suppose your bank balance in pence is on top of the stack: it will be positive or
negative, depending on whether you are in credit or overdrawn. If you use . to print it
out, overdrafts will be printed with a minus sign. This isn't the way banks do it, so
maybe you'd like a word to print out the absolute value of the number (i.e. without
any minus sign) followed by 'CREDIT' or 'DEBIT'. You can visualise what you want to
do with a flowchart :
46
You start at(jstart) and follow the arrows until you reach (^stop) . When you reach a
diamond shaped box, you have a choice of paths to follow, so you need to make
some kind of decision.
We can already translate some stretches of this flowchart into FORTH.
c
start
)
becomes : BALANCE
( balance —)
Print the absolute value
of the balance
Print ‘CREDIT’
Print ‘DEBIT’
becomes ABS.
(recall ABS from Chapter 4)
because CREDIT"
because DEBIT"
stop
because
The remaining problem lies in translating the decision diamond, and for this you
need some new words: IF, ELSE and THEN.
Here is the full FORTH definition:
: BALANCE
(balance —)
DUPABS . 0<
IF
(if balance negative)
DEBIT"
ELSE
(if balance positive or 0)
CREDIT"
THEN
IF makes a decision between two paths, one from IF to ELSE, and the other from
ELSE to THEN. The paths join up again after THEN.
IF bases its decision on the number at the top of the stack (and it throws the
number away afterwards), so this number is called a condition. If the condition is 0
(you should think of 0 as meaning 'false' in this context), it goes to the path between
ELSE and THEN. If the condition is not 0 (think of any non-zero number as meaning
'true'), it goes to the path between IF and ELSE.
47
CHAPTER 9
You can think of IF, ELSE and THEN as meaning:
IF the number on top of the stack was true, follow this path
ELSE if it was false follow this path
THEN afterwards in either case, carry on here.
(If you're used to the computer language BASIC you'll need to adjust your thinking
slightly here.)
To get this decision number on the stack for IF to use, you'd normally use a special
testing word that does some test and leaves 1 (for true) on the stack if the test
passed, and 0 (false) if it failed. The result of such a test, 1 or 0, is called a flag.
We've used one test already, namely 0<. The test here is 'take the top number off
the stack, and test to see if it is negative'. (In case you're riot familiar with this
mathematical notation, < means 'is less than'. If I say '2<3', then you nod your head
sagely and say 'That is true'. If on the other hand I am so rash as to say '3<2', then
you laugh me to scorn, saying 'That is false'. Remember that the narrow end of the <
symbol should point to the smaller number. Also, < really means 'is definitely less
than' because a number isn't less than itself — '2<2' is false.)
Balance negative means 0< test passes
so 0 < leaves 1 on the stack
and IF does IF . . . ELSE path (for DEBIT)
Balance positive or zero means 0< test fails
so 0 < leaves 0 on the stack
and IF does ELSE ... THEN path (for CREDIT)
Note how we start off with DUP so that even after the absolute value has been
printed, the balance is still left on the stack for the 0< test. (Go through BALANCE
drawing pictures of cards to represent the stack. Do it at least twice, once with a
negative balance the operand card that you start off with -- and once with a positive
one. In either case, the net effect on the stack should be to take one card off.)
There are more of these testing words.
= (K, Q — flag) takes the top two numbers off the stack and tests to see if they are
equal.
< (K, Q — flag) takes the top two numbers off the stack and tests to see if the one
second from the top (K) is less than the top one (Q). It must be definitely less, not
equal.
> (K, Q — flag) takes the top two numbers off the stack and tests to see if the one
second from the top (K) is definitely more than the top one (Q).
48
MAKING DECISIONS
0= (K — flag) takes the top number off the stack and tests to see if it is 0.
0< (K — flag) takes the top number off the stack and tests whether it is definitely
negative (less than 0; not 0 itself).
0> (K — flag) takes the top number off the stack and tests whether it is definitely
positive (more than 0; not 0 itself).
Remember that a testing word leaves 1 — for true — on the stack if the test passes,
0 — for false — if it fails.
The words 0=, 0< and 0>, although properly defined words in their own right, have
the same effect as 0 =, 0 < and 0 > so this should help you remember them.
There is a simpler form of IF that misses out ELSE: it just has IF and THEN. You
use it when there is nothing special to do if the number that IF bases its decision on
turns out to be 0 (false). You can think of it as meaning:
IF the number on top of the stack was true, follow this path
THEN afterwards, in either case, carry on here.
Here is an example. The word LUCKY? checks the top of the stack, and if it is 13
replaces it by 12 on the grounds that 13 is unlucky. Of course, it is unlucky, because
LUCKY? Stubbornly replaces it by 12, which will give the wrong answer.
: LUCKY?
( K — 0)
DUP 13 =
IF
DROP 12
THEN
49
CHAPTER 9
Since for numbers other than 13 you don't need to do any more, you don't need
ELSE.
The flowchart looks like this:
A word that is much more useful than you would ever expect is ?DUP ('query
dupe').
?DUP duplicates the top of the stack, but only if it is not 0.
(K — K, K) when K turns out to be non-zero
( 0 - 0 ).
This is most useful just before an IF . . . THEN; you will be amazed how often it
saves you having to say ELSE DROP.
For instance, suppose you want a word ?. to print the top of the stack, but only if it
is non-zero. Without ?DUP you'd need to do
: ?.
DUP
IF
ELSE
DROP
THEN
50
MAKING DECISIONS
but with ?DUP you can define it much more neatly by
: ?.
?DUP
IF
THEN
9
Work through both of these, checking the stack at each step.
Summary
Flowcharts.
Testing words, flags, true and false.
FORTH words: IF, ELSE, THEN, =, <, >, 0=, 0<, 0=, ?, DUP.
Exercises
1. If the top of the stack is 0, then 0= would change it to 1; and if the top is 1 then
0= would change it to 0. This means that 0= can be used to reverse the result of a
test, changing pass to fail and vice versa. This is useful when you want to do
something if a test fails, but nothing if it passes: with 0 = you can reverse the result
and use IF .. . THEN without ELSE.
2. For all the tests where one number is supposed to be less than or more than
another, it has to be definitely less or definitely more: the test fails if the numbers are
equal.
Define a testing word 0>= that is like 0> except that it also passes if the number
is equal to 0. (Hint: think backwards - when is the test to fail? Use Exercise 1 as
well.)
3. What happens if you use IF, THEN or ELSE outside a word definition? (Answer:
the computer doesn't like it.) The reason is that when it gets to IF it is expected to
make its decision, but doesn't yet know where the ELSE and THEN are. When it's
just putting a word definition into the dictionary, the computer doesn't yet have to
make its decision, so it gets a chance to sort out the different paths to go and leave a
description of them in the dictionary.
4. Try defining a word with IF, ELSE and THEN in the wrong order. You will again
find that the computer doesn't like it. It scraps the entire definition (because of
ERROR), so you have to start again from :.
5. When playing threes-and-fives dominoes, you calculate your score in three steps.
First, you count the spots at the free ends of the two end dominoes. (Doubles
count all their spots.)
Next, divide the number of spots by 3, and if there is no remainder score the
quotient.
51
CHAPTER 9
Last, divide the number of spots by 5, and if there is no remainder score the
quotient.
For instance, if the number of spots is 7 then you score nothing, because neither 3
nor 5 divides exactly into 7. If the number of spots is 10 then you score 2, for the
twice that 5 divides into 10. The highest score is from 15 spots (a 5 and a double 5 or
a 3 and a double 6(— you score 5 for the five times that 3 divides into 15, and 3 for
the three times that 5 divides into 15, making 8 in all.
Write a FORTH word SCORE that takes off the stack the number of spots at each
end, and leaves the score: e.g.
4 3 SCORE
leaves 0. Arrange it so that with a double you type DOUBLE: e.g.
4 3 DOUBLE SCORE
leaves 2.
6. The factorial of a number is defined as the product 1*.2* ... as far as the given
number. It is usually written with an exclamation mark, so
1 ! = 1
2 ! = 1*2 = 2
3! = 1*2*3 = 6
4! = 1*2* 3* 4 = 24
etc.
We also define 0! as 1. (There are good mathematical reasons for doing this.)
Type in this word FACT:
: FACT
(n-n!)
?DUP
IF
DUP 1- FACT *
ELSE
1
THEN
We've used here a rather clever technique called recursion, which means that FACT
uses itself. We can do this because
2 ! = 2 * 1 !
3! = 3 * 2!
4! = 4 * 3!
52
MAKING DECISIONS
and so on: so to work out the factorial of some number, we're saying 'First work out
the factorial of the number just before it, and then multiply the answer by the original
number'. We use the same process for the smaller factorial and gradually work our
way down. Naturally this process can't go on for ever, so we make it stop by stating
for a fact that 0! = 1 — this is why we need
IF ... ELSE 1 THEN
7. A more general sort of recursion is when two or more words use each other.
FORTH isn't really designed for this, although it can be done. The problem is that
whichever word is defined first can't use the others — they're not yet in the dictionary
and the computer will give a |
To get round this, you must use REDEFINE. Suppose you want to define words A
and B that use each other.
1. Define a dummy word A for B to use
: A
2. Define B properly — it won't work, of course, because its A is just a dummy,
but you can at least type it in.
3. Type in the proper definition of A.
4. Do
REDEFINE A
so that the dummy A is now replaced by the proper one.
8. The sign of a number is 1 if the number is positive, 0 if it is zero, and -1 if it is
negative.
Write a FORTH word SGN to replace the top of the stack by its sign.
53
Chapter 10
REPEATING
Here's an exercise: given what you know so far in FORTH, is it possible, when
obeying a word, for any part of its definition to be used more than once? The answer
is no. The computer always progresses forwards through the definition, sometimes -
under the influence of IF, ELSE and THEN — skipping round sections.
However, FORTH is actually quite rich in methods of jumping backwards so you
can repeat sections. Here is one example: you want to define a word ++ that will
add together several numbers on the stack, but you don't know how many. One way
is to say that all the numbers must be non-zero, so if you plant 0 on the stack before
you pile on the numbers to be added, ++ can add up numbers until it reaches 0.
We assume that there are at least two numbers to be added, so the method is: add
the top two numbers and look to see if 0 has reached the second from the top yet. If
so, get rid of 0 and we're finished, but if not go back and try again. Here is a
flowchart:
54
REPEATING
Here is how it translates into FORTH:
: ++
( adds together numbers on the stack as far as a 0)
BEGIN
+ ( 0 or next number, sum so far)
OVER 0=
UNTIL
SWAP DROP
UNTIL is the decision maker here. You can put what you like between BEGIN and
UNTIL, but it must culminate in leaving a condition (true or false) on the stack, and
this is what UNTIL bases its decision on. If the condition is true then repeating is over
and the computer carries on with the section after UNTIL. If on the other hand the
condition is false, the computer jumps back to BEGIN.
Let us see how this works with ++. At BEGIN, the stack is supposed to have the
running total on top, then the next number to be added on and then either 0 or the
next number but one to be added. (Is this right the first time round? Only if there are
at least two numbers to be added. Then you can use the first number to start off the
running total. If there's only one number, then our ++ doesn't work properly — we'll
come back to this bug later.)
+ adds the top two together to give a new running total on the top, and OVER gets
the next number to the top so we can check whether it is the 0 yet. The test for
UNTIL is to pass for 0 and fail for other numbers so we use 0=. If it wasn't 0, the
computer goes back to BEGIN; if it was it carries on and drops the 0, just leaving the
sum on the stack.
If you're not exactly clear what's happening to the stack through all this, then try it
out with some examples, writing down what's on the stack at each step in the
calculation.
55
CHAPTER 10
One disadvantage of UNTIL is that the computer must go through the section
between BEGIN and UNTIL at least once. This is obvious when you think about it,
because there's no test at BEGIN to enable it to skip round. A construction that gets
round this uses words WHILE and REPEAT instead of UNTIL, in the form
BEGIN
WHILE
REPEAT
The decision maker here is WHILE: it can decide either to carry on with the section
up to REPEAT (which it does if it finds true on the stack), or to skip that section and
give up the BEGIN . . . WHILE ... REPEAT loop.
Thus there are two main differences between UNTIL and WHILE ... REPEAT:
(i) UNTIL stops repeating when it finds true, but WHILE stops repeating when it
finds false.
(ii) WHILE has a section (from WHILE to REPEAT) that need never be used at all,
because WHILE might find false the very first time.
56
REPEATING
Here is a better version of ++ that uses BEGIN ... WHILE ... REPEAT.
: +++
( adds together numbers on the stack, down to a 0)
0 (to start off the running total)
BEGIN
( now running total on top, next number to add underneath)
SWAP ?DUP
WHILE
+
REPEAT
Just after BEGIN, we have the running total on top of the stack and the next
number to be added underneath. We bring this number to the top to see if it is 0: if it
is not (i.e. if it counts as true) then we add it to the running total and loop back to
BEGIN; if it is 0 we stop. Note again how ?DUP manages to be very helpful without
really trying.
Check that +++ behaves well however few numbers you put on the stack to be
added.
Both these forms, with BEGIN, carry on repeating until (or while) some test that
you've programmed in passes. There are some more that use a word DO, and these
repeat a specified number of times using a counter.
The simplest form is
. . .DO .. . LOOP. . .
(inside a colon definition).
DO takes two numbers off the stack, and they determine how many times the
section from DO to LOOP will be executed. The top number is the value given to the
counter the first time round, and the number second from the top is the limit: the
looping stops when the counter reaches it.
If your colon definition contains
6 3 DO ... LOOP
the section between DO and LOOP will be executed 3 times, and the counter will be
3 the first time round, 4 the second and 5 the third. Note that the counter never
actually reaches the limit and so the limit has to be one more than the last value you
want the counter to take. LOOP each time adds one to the counter, tests if that takes
it up to the limit, and if not jumps back to just after the DO.
To get the value of the counter .- and put it on the stack - you use the word I, so
here is a word NOS to print all the numbers from 0 up to (but not including) the limit it
57
CHAPTER 10
finds on the stack.
: NOS
(limit —)
0
DO
I . ( prints the counter)
LOOP
3 NOS
prints out 0, 1 and 2, and
1 NOS
just prints out 0. What do 0 NOS and -1 NOS do? They print out 0, and this
illustrates two rules:
(i) As in BEGIN . . . UNTIL, the section between DO and LOOP will be executed at
least once, regardless of what the initial value and limit are (so NOS always prints 0).
(ii) LOOP stops looping back when the counter (after having 1 added to it) is equal
to or more than the limit. So when (as in -1 NOS) the limit is -1, after 0 is printed
LOOP takes the counter up from 0 to 1; and since 1 is already more than -1, the
looping stops.
LOOP always adds one to the counter, but there is a variant +LOOP that takes the
top number off the stack (called the step) and adds that to the counter instead of 1.
Rules (i) and (ii) above still apply, except in the case where the step is negative. Then
+LOOP stops if the new value of the counter is equal to or less than the limit. You'll
see why if you think about a word
: COUNTDOWN
-1 10
DO
I . -1
+LOOP
If we didn't have this special rule for when the stop is negative, COUNTDOWN
would only print out 10.
You can have more than one of these loops going at once, as in this word STARS.
STARS prints out a triangle of stars, with one in the top row, two in the next and so on.
The number of rows is expected on top of the stack (we add one to it to allow for
58
REPEATING
the way the limit of a DO loop works).
: STARS
( no. of rows —)
CR 1+ 1
DO
I 0
DO
■■ *n
LOOP
CR
LOOP
Since this means there are two counters running at the same time — one slow
count for the rows, and a faster one for the stars in a row — it raises the question of
which one I gives. The answer is that when you're in a stretch of program that's in
two or more loops, I refers to the counter of the innermost or tightest loop, the one
that appears furthest to the right when you LIST it. The counter of the next innermost
loop is given by a word J. We can illustrate this with STARS.
: STARS
( no. of rows —)
CR 1+ 1
DO
1 0
DO
"| star
■■ *ii
L counting
LOOP
CR
LOOP
J loop
■V
-<
row
>■ counting
>
loop
}
V
J
only row loop
both loops
only row loop
Inside the star counting loop — that is to say on the line with ." *" — both loops are
in operation. There I would give the star counter and J the row counter. However, in
the rest of the row counting loop — on the lines I 0 and CR — only the row counting
loop is in operation, so I gives the row counter and J would give rubbish. Thus I can
refer to different counters in different parts of the program.
There is another word, I', that puts on the stack the limit of the innermost loop.
Note — you can only use I, I' and J in the word definition that contains their
corresponding DO and LOOP.
You can combine all these structures IF — ELSE ... THEN, BEGIN ... UNTIL,
BEGIN ... WHILE ... REPEAT, DO . . . LOOP and DO ... +LOOP — as much as you
like, with one proviso. When they overlap at all they must nest properly inside each
other.
To take an example, suppose we have both a DO . . . LOOP and an IF ... ELSE ...
THEN. The possibilities allowed for combining them are as follows.
59
CHAPTER 10
1. They can be entirely separate — either
DO
LOOP
IF
ELSE
THEN
or
IF
ELSE
THEN
DO
LOOP
2 . The IF ... ELSE ... THEN can be nested inside the DO ... LOOP:
DO
IF
ELSE
THEN
LOOP
3. The DO ... LOOP can be nested inside the IF . .. ELSE ... THEN — either
IF
DO
LOOP
ELSE
THEN
or
IF
ELSE
DO
LOOP
THEN
Note that the IF . . . ELSE ... THEN has two sections, and the DO ... LOOP can be
nested in either one of them, but not in both at once. This type is forbidden:
IF
DO
ELSE NOT ALLOWED
LOOP
THEN
If you're not sure whether a word definition of yours satisfies this rule or not, then
the easiest thing is to try it and see. If it breaks the rule the computer will give ERROR
— and scrap your definition.
60
REPEATING
Next, here are two words used for getting out ahead of time: LEAVE for getting
out of DO ... LOOP, and EXIT for getting out of an entire word.
LEAVE can only be used inside a DO . . . LOOP (or DO . . . +LOOP). It doesn't
leave straight away, but sets the counter equal to the limit so that the next time
LOOP (or +LOOP) does its test it is bound to decide to stop looping.
EXIT can be used anywhere except in a DO... LOOP or DO... +LOOP.
Whatever the word is whose definition contains EXIT, the computer will immediately
give up that word and go on to the next one.
Finally, here are two words that are quite closely connected with I, I' and J, and to
understand them you need to be aware that there are in fact two stacks: the one you
know and love (sometimes called the data stack) and another called the return stack.
When a FORTH word is being executed, the computer needs to remember where to
return to when that particular word is completed. It does so by having a return
address stored on the return stack. If the first FORTH word uses another one, and
that second FORTH word use a third, and the third uses a fourth, the computer can
just stack up return addresses on its way down and use them to find its way back up
to the first FORTH word.
However, the return stack is not only used for return addresses; you can also use it
temporarily for storing numbers from the data stack, and this uses words >R and
R>.
>R (number on data stack — ) is pronounced 'to R', and transfers a number from the
top of the data stack to the top of the return stack.
R> ( — number from return stack) is pronounced 'R from' and is the reverse of >R. It
transfers a number from the top of the return stack to the top of the data stack.
Because the return stack is normally used for return addresses, >R and R> must
balance each other within any given word definition; also, you can't use EXIT
between >R and R> because EXIT expects to find a return address on the return
stack.
Now the reason why these are connected with I etc. is that a DO loop stores its
limit and counter on the return stack (with the counter on top). Thus all that I, I' and J
really do is copy a specific return stack entry to the data stack: I copies the top one, I'
copies the second, and J copies the third. This means that you can also use them for
copying the numbers put on the return stack by >R. It also means that >R and R>
must balance each other within a DO loop.
Summary
BEGIN . . . UNTIL
BEGIN . .. WHILE . . . REPEAT
DO ... LOOP
DO.. .+LOOP
LEAVE is used to cut short a DO ... LOOP
EXIT is used to cut short a word
I, I’, J, >R, R>
61
CHAPTER 10
Exercises
1. Here is a word PRIME to test whether a number is prime or not. (A number is
prime if the only numbers that divide into it exactly are 1 and itself. 2, 3 and 5 are
prime, but 4 is not, because 2 divides into it exactly.) PRIME leaves the tested
number, its operand, on the stack, with another number on top. This other number is
0 for a prime, otherwise not 0.
: PRIME
( number number, 0 for a prime)
2
BEGIN
( number, number to try to divide into it)
OVER OVER DUP * < 0=
WHILE
OVER OVER MOD 0=
IF
EXIT
THEN
1 +
REPEAT
DROP 0
At BEGIN, the stack has the number itself second from the top, and above that a
trial divisor to divide into it. The trial divisor starts at 2 and is increased by 1 each time,
but it is only necessary to take it as far as the square root of the number (why?). Thus
we loop round while the square of the trial divisor is no bigger than the number itself.
When we've tried them all and none of them divide we drop the last trial divisor and
stack 0 because we know the number is prime.
On the other hand, if some trial divisor does divide, we exit PRIME straight away,
with the number and the (non-zero) trial divisor on the stack.
Write a word PRIME? that is a proper testing word for primeness — it is to replace
the top of the stack by 1 (true) if it was a prime, 0 (false) if not. (Hint: use PRIME in it.)
Here is a word PRIMES to print out all the primes up to a limit from the stack. It
uses your PRIME?.
: PRIMES
(limit —)
1
DO
I PRIME?
IF
I .
THEN
LOOP
62
REPEATING
2. Raising a number to a power is the process of multiplying together several copies
of the number - the power is the number of copies. For instance, raising a number to
the power 2 involves multiplying the number by itself (two copies, one multiplication),
otherwise known as squaring it.
6 raised to the power 2 6 * 6 = 36
Similarly
6 raised to the power 3 6*6*6 = 216
This would normally be written as
t power
Number
but a common alternative is 6 |3.
Write a FORTH word T to raise a number to a power (number, power — answer).
What does your T do if the power is 1 or 0? A number raised to the power 1 is just
the number itself, and there are sound mathematical reasons for saying that a
number raised to the power 0 is 1. Get your t to do this properly, and then compare
it with ours here.
: T
( number, power number raised to power)
1 SWAP ?DUP
IF
0
DO
OVER *
LOOP
THEN
SWAP DROP
Note how the ?DUP, IF and THEN get over the problem with DO . . . LOOP that it
has to be gone through at least once.
3. Here's a useful trick with BEGIN and UNTIL. If you want to do something over
and over again for ever (or until you press BREAK) you use BEGIN and 0 UNTIL.
Unless you interrupt it, this will repeat until the sea runs dry, rocks melt in the sun,
cocks lay eggs and 0 becomes non-zero, e.g.
: BORE
I just go
BEGIN
on and
0
UNTIL
63
Chapter 11
SOUND
The Jupiter Ace has a loudspeaker built into it, so you can liven your words up
with occasional bleeps or even tunes. The word to use is BEEP which expects two
numbers on the stack. The top one is the length of the note in milliseconds
(thousands of a second), and the second from the top specifies the pitch of the note.
Technically, this pitch number is the period in units of 8 microseconds, but you'll
probably find it easier to use this table. It shows the pitch numbers for the notes of
seven octaves at semitone intervals.
c
1911
956
478
239
119
60
30
B
2025
1012
506
253
127
63
32
B b A #
2145
1073
536
268
134
67
34
A
2273
1136
568
284
142
71
36
A b G #
2408
1204
602
301
150
75
38
G
2551
1276
638
319
159
80
40
F # G
2703
1351
676
338
169
84
42
F
2863
1432
716
358
179
89
45
E
3034
1517
758
379
190
95
47
E b D*
3214
1607
804
402
201
100
50
D
3405
1703
851
426
213
106
53
C # D b
3608
1804
902
451
225
113
56
C
3822
1911
956
t
478
t
239
t
119
t
60
low C middle C upper C top C
If you're just interested in bleeps and grunts, then all you really need to remember is
that the smaller the number, the higher the note. As a very rough rule,
pitch numbers in the 10s give high notes,
pitch numbers in the 100s give medium notes,
pitch numbers in the 1000s give low notes,
pitch numbers in the 10000s give series of clicks.
If you want to play tunes, you'll have to go to a bit more effort. We'll show you how
to program 'Three Blind Mice'.
For each note you need the pitch number and the time in milliseconds. Rather than
give the time in milliseconds for every single note, it's a good idea to give the length
of the shortest note in the tune, store this length as a variable, and specify the
64
SOUND
lengths of the other notes as multiples of this. Thus if 'Three Blind Mice' is taken to
be in 6 /s time, the shortest note is a quaver.
Define words
100 VARIABLE QUAVER
: N
( pitch number, length in quavers —)
QUAVER @ * BEEP
Now a dotted crotchet (three quavers' worth) at middle C would be
478 3 N
We have made the name N very short, because you'll be typing it a lot. Using
QUAVER has the added advantage that the tune can be speeded up or slowed down
by changing the value of QUAVER. The value 100 gives '/n second for each quaver,
which is quite fast.
Now for the tune itself. There is quite a lot of repetition in it, so we have reduced it
to three parts:
Part 1
—
v o
>
3
} 8
—I—
•
s%
Three blind mice
is played by the FORTH word
: PARTI
(Three blind mice)
190 3 N 213 3 N 239 6 N
(We've pitched it an octave higher than it's actually written.)
Part 2 -
n
r
i—«
see how they run
65
CHAPTER 11
is defined by
:PART2
( See how they run)
159 3 N 179 2 N 179 1 N
190 5 N
(We've had to cut off the tail from the final dotted minim — properly 6 quavers —
because when Part 2 is repeated it is cut short at the end by Part 3.)
Part 3
They all ran after the farmer's wife
etc
:PART3
( They all ran after the farmer's wife)
159 1 N 1192 N 119 1 N
127 1 N 142 1 N 127 1 N
1192 N 159 1 N 1592 N
These can now be pieced together in
: MICE
PARTI PARTI
PART21191 N
PART2
PART3 PART3 PART3
1791 N PARTI
1
Summary
FORTH word: BEEP.
Exercises
1. It would be nice to be able to specify the pitch in semitones above some standard
note, rather than as the pitch number we have described. To do this, it is necessary to
explain a bit about how the two are related.
To add a given musical pitch to a note, you must multiply our pitch number by
66
SOUND
some corresponding number. (Such a relationship is called logarithmic.) Thus to raise
a note up an octave, you multiply its pitch number by (divide it by 2). You can see this
quite clearly in the table. To raise a note up a semitone, you multiply the pitch number
by the twelfth root of 2 (because there are twelve semitones in an octave), and this is
0.94387431, or approximately 17843/18904.
This leads to a method for calculating pitch numbers from semitones:
(i) Start off from a rather low note with a known pitch number — say 3822 for the
lowest C in the table. Work out the note wanted in terms of semitones above this low
note.
(ii) Work out the number of semitones as some octaves plus, at most, 11
semitones.
(iii) For each semitone, multiply the pitch number by 17843/18904, and then for
each octave divide by 2. This gives the pitch number you want.
Here is a word to use this method. It takes off the stack the pitch in semitones
above middle C, and leaves as its result the pitch number for BEEP.
: SEMS
( semitones above middle C — pitch number)
36 + ( semitones above bottom C)
12 /MOD SWAP ( no. octaves, no. spare semitones)
3822 SWAP ?DUP
IF
( multiply by 17843/18904 for each spare semitone)
0
DO
17843 18904*/
LOOP
THEN
SWAP ?DUP
IF
( divide by 2 for each octave)
0
DO
2 /
LOOP
THEN
Try SEMS with BEEP. Unfortunately, because of all the arithmetic, notes like B and
B b have a noticeably longer pause in front of them than C. In Chapter 20 we shall see
a way of storing the powers of 17843/18904 separately so that only one
multiplication and division is needed for the spare semitones.
2. If you want to approximate 12 -/F(0.94387431 ...) as one whole number divided
by another, you'd probably choose 9439/10000. 17843/18904 is much more accurate
67
CHAPTER 11
so how do you think I got it? I can't be bothered to explain it here, but if you're really
interested, the answer lies in the mathematical technique of continued fractions.
3. Try BEEP with small operands.
The smallest pitch value that gives a high note is 7; for anything smaller than that
the computer gets it wrong and gives a series of clicks. Whether you can actually
hear the note with pitch value 7 depends on your ears. Some people can and some
can't.
The smallest note length that you can use depends on the pitch value: if the length
is less than one cycle at that pitch (i.e. if it is less than V 125 of the pitch value) then the
computer will get it wrong and put out a note that's the right pitch, but much too long.
Notes have to be pretty short before they give problems here.
68
Chapter 12
THE CHARACTER SET
The characters referred to here are all the letters, digits, punctuation marks and other
kinds of symbols that the computer knows about. Each one has a code between 0
and 255, called its ASCII code (ASCII stands for American Standard Code for
Information Interchange') and the computer sees the characters in terms of their
codes.
To see the whole range of characters, define a word
: CHARS
( displays the character set)
256 0
DO
I EMIT
LOOP
EMIT takes an ASCII code off the stack, and prints the corresponding character on
the TV screen. You won't need much convincing that they fall into two groups: the
characters with codes 0 to 127 are white on black, and the characters with codes 128
to 255 are black on white (black on white is called inverse video). In fact if two ASCII
codes differ by 128, then the two characters are the same except that black and
white are swapped over from one to the other: e.g. the normal A (which would
appear as white on black on the TV) has ASCII code 65 and the inverse A (black on
white) has ASCII code 65+128=193.
The characters with codes 32 to 127 are pretty well standard ASCII as used on
computers all over the world: the only differences are details like where to put £ on a
British computer.
The characters with codes from 0 to 31 are not standard. In ASCII they are set
aside as control characters, which don't print anything but carry some message like
'move on to a new page' or 'ring a bell.' On the Ace, only one of these is used like
this: the character with code 13 means 'carriage return' (i.e. move to the left hand
margin of the next line). The rest are used for graphics characters, which can be
either provided for you by the Ace or designed specially by you yourself.
The Ace graphics are patterns of black and white squares. Imagine the space for a
character being divided into four smaller squares like a slice of Battenberg cake:
69
CHAPTER 12
If each of the smaller squares can be either black or white, then there are
2x2x2x2=16 possibilities, and the Ace provides all of these using the characters
with cods 16 to 23 and 144 to 151 (the inverse video versions of the first eight).
Character
Code
Character
Code
■
16
□
144
L
17
145
a
18
n
146
H
19
B
147
p
20
□
148
E
21
a
149
22
s
150
■
23
a
151
You can type in these characters from the keyboard by using graphics mode. If you
press shifted 9 (marked GRAPHICS) then the cursor will change to a @ and the digit
keys will give the graphics characters marked on them. These are the ones with
codes 16 to 21 - if you want the other eight you must use shifted 4, INVERSE VIDEO,
as well. (It doesn't matter in what order you press GRAPHICS and INVERSE VIDEO
because they work quite independently.)
You will find that the other keys also give graphics characters. These have the
same patterns as the ones just described, but they mostly have different codes —
there are in fact four sets of the eight non-inverse graphics characters, with codes
from 0 to 7, 8 to 15, 16 to 23 (the ones you'd normally use) and 24 to 31. Since you
are free to redesign the shape of any character you like, you can use the spare
graphics characters 0 to 15 and 24 to 31 for your own inventions.
There is a way of calculating the result of pressing a key when in graphics mode:
1. Work out the ASCII code for the key you press.
2. Divide it by 32.
3. The remainder is ASCII code for the character you get in graphics mode.
4. — unless you're also in inverse video mode, when you add 128 to get the ASCII
code.
For instance, 'a' has ASCII code 97, and 97 + 32=3, remainder 1, so a graphics
70
THE CHARACTER SET
mode 'a' is the character with ASCII code 1. You'll probably find it easier to remember
that in graphics mode a or A gives code 1, b or B gives code 2 and so on up to z or Z
which gives code 26.
Here's how to redefine characters. Let us say that when you write a program to
play Space Invaders you need a picture of a train instead of a space ship (to avoid
infringing anyone's copyright). All the characters use an 8x8 grid of dots (you can see
them if you look at the television picture very closely), so the first step is to design an
engine on an 8x8 grid of dots.
Mxlxx
X
_ xxxxx
b< x
lx x
txjxlxxxxxx
m
Here a cross means a white dot (on the television) and a blank means a black dot.
The next step is to decide what ASCII code you want your engine character to have
— let us say 1, so that it is on the A key in graphics mode. You could use any code
from 0 to 127 (not the codes from 128 to 255, because their characters are
automatically the inverse video versions of the first lot). However, it's best to start off
with those from 0 to 15 or 24 to 31 because they're not used for anything else, and of
these 0 and 13 are much less convenient than the others. 0 is used in the input buffer
to partition it into computer lines, and 13 causes a carriage return when it is EMITted.
Now define this word:
: GR
( 8 row numbers, ASCII code — )
8 * 11263 + DUP
8 +
DO
I C! -1
+LOOP
GR expects 9 numbers on the stack. The top one is the ASCII code of the character
you want to redesign — 1 in our case — and the other eight are numbers describing
the eight rows of dots. The top row is furthest down in the stack, and the bottom row is
second from the top in the stack. To stack these eight numbers, type in
71
CHAPTER 12
2 BASE C!
00000100
11110010
00010010
00011111
00100001
00100001
11111111
01100110
DECIMAL
(The precise significance of BASE and DECIMAL will be explained in Chapter 16. We
are using binary notation, in case you already know what that is.)
As you can see, we've just changed the Xs in the dot pattern to Is, and the blanks
to 0s.
Now all you need to type is 1 (for the ASCII code) and GR, and the character with
code 1 will be redesigned as an engine.
The easiest way to see your engine is to go into graphics mode with shifted 9, and
type A. If you press INVERSE VIDEO (shifted 4) as well, then A will produce an
inverse engine (ASCII code 129). You can now use the engine just like any other
character, putting it after or by using 1 EMIT. You can even define words with the
engine in their names, for instance:
: 3J
." Choo-choo"
To explain how GR works, suppose you have a row of eight dots. Each dot
can be either black or white, so the number of possibilities is
2 8 =2x2x2x2x2x2x2x2=256 and any given row pattern can be coded as a byte.
The complete pattern of dots, made of eight rows, can be coded into eight bytes and
stored in eight of the Ace's memory boxes.
The Ace uses part of its memory (with addresses between 11264 and 12287) to
store the patterns for the characters with codes up to 127 (the rest are the inverse
video versions). They are stored in order of ASCII code, so the top row of a given
character is stored at address
11264+8* the ASCII code
and the bottom row is seven bytes further on (Why seven and not eight? Work out
where the six intervening rows go.)
72
THE CHARACTER SET
Here is how this table of patterns starts off.
Address Row stored there
11264 Top row for ASCII code 0
11265 2nd row for ASCII code 0
11266 3rd row for ASCII code 0
11267 4th row for ASCII code 0
11268 5th row for ASCII code 0
11269 6th row for ASCII code 0
11270 7th row for ASCII code 0
11271 Bottom row for ASCII code 0
11272 Top row for ASCII code 1
11273 2nd row for ASCI I code 1
11279 Bottom row for ASCI I code 1
To redesign the character with ASCII code 1, we must put our own numbers in
boxes 11272 to 11279. GR calculates these addresses and fills in the boxes, starting
at 11279 and working back.
GR uses a new word C! (pronounced 'C store') to fill in the boxes.
C! is just like ! except that it only uses one box at the given address so it can only
store a byte (byte, address — ). Remember that! uses two neighbouring boxes.
Similarly,
C@ (pronounced 'C fetch') is just like @ except that it fetches a byte from a single
box (address —byte).
It is quite common in FORTH for there to be two related words, one to act on full
numbers (like @ or !) and one to act on single bytes (like C@ or C!(. Because the
ASCII code for a character is a single byte, the names of the single byte versions
often start with C for 'character'.
Note — it's a peculiar feature of the piece of memory containing the dot patterns
that you can write to it, but you can't read back. Only the part of the computer that
makes the television signal can read from this memory.
A useful word connected with characters is ASCII — by using it you need hardly
ever look up ASCII codes. ASCII takes the next word from the input buffer, and
stacks the ASCII code for the first character in that word. For instance,
ASCII abcde .
prints 97, the ASCII code for 'a'. Notice that 'abcde' doesn't need to be a defined
73
CHAPTER 12
word; it is just a word in the sense that it is separated from the rest of the line by
spaces.
One character that ASCII won't work with is the space (why not?). It's best to
remember that space has ASCII code 32.
Some more words connected with characters are SPACE, SPACES, CLS, AT and
TYPE.
SPACE (—) EMITs a space.
SPACES ( n —) takes the top number off the stack and EMITs that number of
spaces — if the number is positive.
CLS ( —) clears the television screen. (Note — it also reduces the input buffer to a
single line on the television screen, so you can't have too many words after CLS if it
is executed from the input buffer.)
AT ( line, column —) takes two numbers off the stack. The second from the top
specifies a line on the screen -- 0 for the top line down to 22 for the bottom line but
one. The top of the stack specifies a column number in that line - 0 for the leftmost
column, 31 for the rightmost. After AT, the next lot of printing on the screen will start
at that line and column.
TYPE ( address, number of characters —) writes to the screen some characters
stored in memory. Since a character is coded as one byte, it can fit into a single
memory box and it is often convenient to store characters this way. TYPE writes all
the characters from a group of neighbouring boxes, finding on the stack the number
of characters and the address of the first.
Summary
The character set and ASCII codes
How to redesign characters
FORTH words — Cl, C@, EMIT, ASCII, SPACE, SPACES, CLS, AT, TYPE
Exercises
1. Make the character with code 2 into a carriage:
IXM.
X
X
X
[xjxjxjxxxjxjxl
74
and try this word, GO.
THE CHARACTER SET
: GO
(whistle length — whistle length)
BEGIN
CLS 22 0
DO
32 0
D ° IA jrWWi ■■
DUP 200 SWAP BEEP ( whistle)
LOOP
LOOP
0
UNTIL
J
GO requires a number on the stack that determines how fast the train moves.
2. Define a word
: CODEA
ASCII abode .
If you LIST this, you'll find that 'bode' has been dropped — but they weren't really
relevant anyway.
Consider also what happens when you run CODEA. We said that ASCII takes a word
from the input line and stacks an ASCII value from that, but that's not actually
what it's doing here. In fact it's stacking an ASCII value from the word that was in the
input line when CODEA was defined. The difference is subtle but interesting,
because it means than ASCII behaves differently when it's inside a word definition.
Of course, the difference is intended to make it do what you need it to do.
3. Design chess pieces, the suits from a pack of cards, the Greek alphabet, anything
you can think of.
4. If you're used to the computer language BASIC, you'll know about its function
TAB. There is no corresponding word in Ace FORTH, but you can easily define one:
: TAB
(tab stop — )
15388 @ - 31 AND SPACES
(Don't worry about how this works for the moment.)
75
CHAPTER 12
TAB takes one number off the stack, and uses it as a column number between 0
and 31. (It the number is 32 or more then TAB divides it by 32 and takes the
remainder.) TAB then EMITs just enough spaces to ensure that the next thing printed
will go in that column — either in the same line or the next one.
As an example of how to use TAB, this word TABLE takes one number off the
stack and prints the number from 0 to just short of the given number, arranged neatly
in four columns.
: TABLE
(limit )
0
DO
I 8 * TAB
I .
LOOP
CR
5. Experiment with TYPE.
0 100 TYPE prints a lot of rubbish. The bytes starting at address 0 are not
characters at all, although TYPE doesn't realise this. They're actually coded
instructions built into the Ace.
8192 500 TYPE is quite interesting, because 8192 is the first address of the memory
that contains the television picture. TYPE is both reading from the television
picture and writing to it.
Try writing your own version of TYPE, using C@, EMIT and a DO loop.
76
Chapter 13
PLOTTING GRAPHS
The Battenberg graphics we described in Chapter 12 can be used to cover the screen
in any pattern of the black and white quarter squares that you care to make up. To
take the hard work out of this, Ace FORTH has a word PLOT that enables you to put a
black or white quarter square anywhere you like on the screen. Any one of these
quarter square positions on the screen is called a pixel (standing for picture element),
so the screen is 64 pixels wide and 48 pixels high. However, you can't use the
bottom two rows of pixels because that area is reserved for the input line, so the part
you can use is actually only 46 pixels high.
To specify a pixel, you need two numbers, called its coordinates. The first, called its
x-coordinate, is the distance across (remember X is a cross) to the pixel from the
left so that a pixel on the left-hand edge has x-coordinate 0 and a pixel on the
right-hand edge has x-coordinate 63. The second number, the y-coordinate, is the
distance up (wise up -- X is a cross) to the pixel from the bottom so that a pixel just
above the bottom line reserved for the input buffer has y-coordinate 0, and a pixel
right at the top has y-coordinate 45.
The diagram opposite can be used for working out pixel coordinates; it also
compares them with the line and column numbers used by AT.
Having found a suitable pixel and worked out its coordinates, you must then decide
what to do with it. There are four possibilities, or plotting modes, namely
Set it black (unplot) (0)
Set it white (plot) (1)
Leave it alone (move) (2)
Whatever it was before, change it (change) (3)
Each has a code - the number afterwards in brackets.
PLOT needs three numbers on the stack: (x-coordinate, y-coordinate, plotting
mode —).
Try
30 20 1 PLOT
which will make a little white square appear near the middle of the screen.
Now the best thing to do is experiment with various coordinates and plotting
modes, so you'll do CLS and then lots of PLOTs. But think for a minute: as you know,
when the Ace obeys what you've typed in, it copies it to the top part of the screen as
a record of what it's done; and afterwards it prints 'OK'. These are going to spoil you
77
Pixel x-coordinates
Lines
You cannot AT or PLOT
on the bottom line
W roroio—»
Kj-*ococo'^a>ai-t^coro
<
ocooo-^CDtn^corso—*o
o
o_
c
3
m
Pixel y-coordinates -^
78
An example: this is
the pixel (57,32)
PLOTTING GRAPHS
careful plotting somewhat, so it would be nice to have an invisible mode in which your
typing is not copied up, and the computer doesn't write 'OK'.
You can get this on the Ace, using a word INVIS (for invisible); to undo its effect,
you use VIS. Type in
INVIS CLS
and try plotting in various ways at various pixels. See if you can draw pictures.
Summary
Pixels and x and y coordinates
FORTH words: PLOT, INVIS, VIS
Exercises
1. Here is a word DRAW that draws fairly straight lines for you by plotting pixels. It
has three operands
( x, y, plotting mode — )
The starting point for the line is the last pixel PLOTted or DRAWn to, and the
finishing point is x pixels across and y pixels up from the starting point, x and y are
thus very like the usual x- and y-coordinates of a pixel, except that they are measured
from the starting point of the line instead of from the bottom left-hand corner of the
screen. One consequence of this is that they might be negative.
For instance,
30
5
1
PLOT
10
10
1
DRAW
- 10
10
1
DRAW
-10
-10
1
DRAW
10
-10
1
DRAW
draws a diamond shape.
. 3
I've written the numbers here to show the order in which the sides are drawn.
DRAW uses some subsidiary words, DRAW1, SGN, DIAG, SQUARE and STEP.
79
SGN
0> DUP + 1 -
CHAPTER 13
DRAW 1
ROT ROT OVER SGN OVER SGN
4 ROLL ABS 4 ROLL ABS
OVER OVER <
ROT ROT 3 PICK
IF
SWAP
THEN
DIAG
6 PICK 6 PICK
SQUARE
4 PICK
IF
0 6 PICK
ELSE
6 PICKO
THEN
STEP
15408 C@ + SWAP
15407 C@ + SWAP
9 PICK PLOT
DRAW
DRAW1 2 PICK DUP 2 /
SWAP ?DUP
IF
0
DO
OVER + DUP 4 PICK >
IF
3 PICK - DIAG
ELSE
SQUARE
THEN
80
PLOTTING GRAPHS
STEP
LOOP
THEN
70
DO
DROP
LOOP
To explain how DRAW works, let m and n be respectively the larger and smaller of
lx| and |yl (the absolute values of x and y). We build up the line using two sorts of
steps: a diagonal step that moves one pixel in both x and y directions, and a square
step that moves in only one direction i.e. vertically or horizontally. If we mix together
as evenly as possible n diagonal steps and m — n square steps then we move m
pixels in one direction and n in the other - which is what we want.
DRAW1 works out what these two sorts of steps are. It replaces x, y and the
plotting mode by six numbers on the stack as follows (starting at the top).
n, the smaller of lx| and |y|.
m, the larger of |x| and |y|.
A flag to show which of |x| , and |y| was the larger - 0 if |x| was larger and a square
step moves horizontally, 1 if |y[ was larger and a square step moves vertically. (If
|x| = |y| then no square steps are needed so it doesn't matter what the flag is.)
The y part of a diagonal step (1 or -1).
The x part of a diagonal step (1 or -1).
The plotting mode.
DIAG copies the two parts of a diagonal step to the top of the stack (both parts are
1 or -1), SQUARE copies the two parts of a square step to the top (one part is 0. the
other is 1 or -1).
STEP takes the two parts of a step, as left by DIAG or SQUARE, and uses them to
plot the next point on the line. STEP needs to know where the previous point was
PLOTted, and it does this using two system variables, one byte each, at addresses
15407 and 15408. Whenever PLOT is used, it stores its x-coordinate in the byte with
address 15407, and its y-coordinate at 15408. STEP later reads these back.
DRAW mixes the n diagonal steps evenly with them n square steps. To see
how, imagine a Monopoly hoard with m squares round the outside, and the dice
rigged so that you always throw n. If you throw the dice m times, you will move m x n
squares round the edge, i.e. right round the board n times. Out of these m throws,
you will pass GO n times and not pass GO m —n times and these will be mixed as
evenly as possible.
The main part of DRAW is concerned with imitating this process. It uses an extra
number on the stack, between 1 and m, to represent the position on the board. It
loops round m times for m throws, and each time adds n to this position number. If
the answer is more than m then it has passed GO so it subtracts in and does a
diagonal step. Otherwise, it does a square step.
81
CHAPTER 13
2. Experiment with DRAW using the different plotting modes. This shows a use for
plotting mode 2 - it updates the PLOT position without affecting any pixels.
Plotting mode 3 is more useful than you'd think, because of the property that if you
PLOT or DRAW the same thing twice over you get back to exactly what you started
off with.
For instance, try (after INVIS CLS)
0 24 1 PLOT 63 0 1 DRAW
32 0 1 PLOT 0 45 1 DRAW
If you wanted to erase the second line, the natural thing to try is
32 0 0 PLOT 0 45 0 DRAW
but this leaves a hole in the first line.
Now try the same all over again, but using plotting mode 3 throughout. When both
lines are present there is a hole where they meet, but that isn't so serious; and when
you erase the second line (plotting mode 3 again) the hole is filled in.
3. Clear the screen and try
0 0 3 PLOT 63 20 3 DRAW and then
-63 -20 3 DRAW
in an attempt to erase the line.
It doesnt work, because the irregularities in the line are not the same when you
reverse.
When you erase a line, always go over it in the same direction as you originally
drew it.
4. Something that is often useful is a random number generator, a word whose
result is random like the toss of a coin or the spin of a roulette wheel. In fact it is
difficult for a computer to produce truly random numbers, because they always obey
their instructions rigidly and predictably. It is easier to produce pseudo-random
numbers, numbers that follow a rule that, although fixed, is sufficiently complicated
to appear random. Here is an example.
0 VARIABLE SEED
: SEEDON
( — next value of SEED)
SEED @. 75 U* 75 0 D+
82
PLOTTING GRAPHS
: OVER OVER U< - -
1- DUP SEED !
: RND
(n —pseudo random no. between 0 and n—1)
SEEDON U* SWAP DROP
: RAND
( value for seed — )
?DUP0=
IF
15403 @ SWAP
THEN
SEED !
Never mind how these work; you'd better just treat them as recipes.
Each time SEEDON is executed, it uses the old value of SEED to produce a new
value, which it also leaves on the stack. If you use SEEDON 65536 times then SEED
gets back to the value it started with and the whole cycle starts all over again, but the
order in which SEED takes its 65536 values is complicated enough to look random.
RND has one operand, and uses SEEDON to produce a pseudo-random number
less than this operand. For instance, 6 RND produces a result between 0 and 5, and 6
RND 1+ produces a result between 1 and 6 — what you'd use for computerized dice.
RAND you could probably do without most of the time. It sets SEED with the value
from the top of the stack, so that you know how the sequence of random numbers
will start off. This is useful for debugging, because it means you can have the same
pseudo-random numbers each time you test your program. An extra facility in my
RAND here is that if you say 0 RAND, SEED is initialized using a system variable that
says how many television frames have been displayed (50 each second) since the
computer was switched on. This is more truly random.
5. Try this, using RND and so on from Exercise 4.
: MEASLES
( plotting mode — plotting mode)
CLS
BEGIN
64 RND 46 RND
3 PICK PLOT 0
UNTIL
83
Chapter 14
SAVING PROGRAMS ON TAPE
As you know, when you turn the Ace off it forgets everything except what was built
into it in the factory. However, this doesn't mean that you have to leave it on all the
time, because you can save your own programs on a cassette tape.
For this, you need an ordinary cassette tape recorder connected to the Ace as
described in Chapter 3.
You will also need a cassette tape. There are some advantages in using a short one
(for instance you don't have to search so far to find your program), but it's not vitally
important. Cheap quality tape will probably work, but if you have trouble you might
get better results with a better tape.
Now type in your favourite words, e.g.
:FLATTER-ME
CR I sincerely belive you’re the"
CR . " most wonderful human being I’ve"
CR . " ever met. You’re really"
To save this on tape, type in
SAVE CREEP
but don't press ENTER yet. SAVE is the word that does the saving ; and CREEP is the
name that will identify the program when it's on tape. You don't have to specify that
you're saving FLATTER-ME, because SAVE just saves the entire portion of the
dictionary that was defined by you (rather than being built-in to the computer). CREEP
does not refer to a defined FORTH word: it's just a label that you choose.
Now wind the tape to a place where, first, you know it's proper tape and not just
plastic leader at the beginning, and, second, if there was anything recorded there
before, you don't mind erasing it. Start it recording, and then press ENTER. After
about 5 seconds you will hear two quick bursts of sound through the loudspeaker,
and OK will come up. The sound was the way the computer coded the dictionary so
that it could record it on tape.
Now as far as the computer is concerned, it's saved the dictionary, but you must
always make sure that it completed its journey to the tape: you do this with a word
VERIFY.
Wind the tape back to just before where you saved the dictionary, turn the tone
control (if there is one) right down and turn the volume up to about three quarters
84
SAVING PROGRAMS ON TAPE
maximum. Type in
VERIFY CREEP
(and ENTER) and start the tape playing back. When the tape reaches your saved
dictionary, you should see a message saying 'Diet: CREEP' and, about 3 seconds
later, OK. If this happens, then well done! If not, you can stop it by pressing the space
key.
What to do if it doesn't work.
1. Carry out all the checks mentioned in Chapter 3 for when you have trouble
loading.
2. Listen to the recording by playing it back through the tape recorder's own
loudspeaker (you'll have to unplug the lead from the earphone socket on the tape
recorder). You should hear first, a high-pitched whine like a mosquito for 5 seconds;
second, a short (less than a second) burst of sound evocative of lazy summer
Sundays near Heathrow; third, more of the high-pitched whine (less than a second
this time); and fourth, back to Heathrow for about a second.
The mosquito is a leader to let the tape recorder get used to the volume level, and
the Heathrow sound is the actual information - first a header saying that it's a
dictionary called CREEP, and various other snippets of information, and then the
dictionary itself.
These should all be unpleasantly loud if you turn the volume right up. If you can't
hear anything at all then for some reason the signal simply hasn't got through to the
tape recorder. One possibility is that the plugs on the leads to the computer don't fit
quite properly into the tape recorder sockets. Try pulling them out just a fraction of an
inch to see if they settle down into a more natural position, and then try saving again.
If the noises sounded all right then try verifying again with the volume turned right
up. If the volume's too quiet then the computer won't pick up the signal properly,
while if the volume is too loud the signal may get distorted (of the two, this is less
likely). If you can't get it to work after trying three or four different volume settings,
try saving again.
If you still can't get it to work, you're not having a very lucky day. Vent any surplus
aggression on the cat and go to bed. Tomorrow you can buy a much better tape,
which is bound to work, and maybe either clean the tape heads on the recorder or
borrow a different tape recorder from a friend.
Once you've saved the program and verified it, you can load it back by following the
instructions in Chapter 3 - this is really pretty much like verifying.
Remember that when you load a dictionary in from tape, it doesn't erase the
dictionary that's already in the computer; it just gets added on at the end. This means
that you can store small packages of words separately on tape and load in only those
that you happen to need. If you try to load in too much, you get ERROR 10, meaning
'tape error'. There are a few other loading faults that can give ERROR 10 - see
Appendix B for more information.
SAVE, VERIFY and LOAD are used for dictionaries stored on tape. You can also
85
CHAPTER 14
save information more crudely by saying how many bytes you want to save, and
what the memory address is of the first one. Instead of SAVE, VERIFY and LOAD,
you use BSAVE, BVERIFY and BLOAD.
For instance, suppose you want to save a copy of the television picture. This has
768 bytes starting at address 8192, so you use
8192 768 BSAVE TVPIC
Again, TVPIC is just a name to go on tape. The mechanical operations with BSAVE,
when to turn the tape recorder on and off, and so on, are the same as with SAVE.
This particular case is one of the rare occasions when you wouldn't verify
immediately after saving. The verifying itself changes the screen (by printing up
'Bytes: TVPIC' when it finds the file on tape), so the screen will no longer be the same
as it was when you changed it.
To load back TVPIC, the obvious thing to say is
8192 768 BLOAD TVPIC
and this will in fact work. However, since you are already loading back TVPIC to
exactly the same place as it was saved from, you could also use
0 0 BLOAD TVPIC
The rules here are:
1. The first number (i.e. the number that BLOAD finds second from the top of the
stack) is the address in memory where you want to start loading back to. It doesn't
have to he the same as the address where the file was saved from, but if it is you can
use 0 instead.
2. The second number (i.e. the number that BLOAD finds at the top of the stack) is
just a safety precaution. If you forget how many bytes were saved in TVPIC, then it
could be dangerous to load it back in — you might find it overwriting memory you
wanted to keep. This second number specifies the maximum number of bytes you
are prepared to have overwritten. If the file turns out to be bigger than that, then it
won't be loaded at all and you'll get ERROR 10.
If you specify 0 for this number, then it means you don't care how big the file is (or
you know that it's safe); you want it loaded anyway.
Both these rules apply to BVERIFY as well as to BLOAD.
Remember that dictionary files and bytes files are 7,uite different, and that the Ace
tells you this when it's reading the tape by writing either 'Diet:' or 'Bytes:' in front of the
name. You can't use LOAD to load back a bytes file, nor BLOAD to load back a
dictionary file.
Summary
Tape files — dictionary files and bytes files
FORTH words: SAVE, VERIFY, LOAD, BSAVE, BVERIFY, BLOAD
86
SAVING PROGRAMS ON TAPE
Exercises
1. Having saved the bytes file TVPIC (from address 8192), try
9216 0 BLOAD TVPIC
to load it back at address 9216. It will change the television picture more or less as
before.
So where is the television picture really? At 8192 or 9216? The answer is both.
These are, as it were, front door and back door addresses for the same memory
boxes.
The front door addresses are 8192 to 9215. When you knock here the computer
serves you immediately, even if it means neglecting the television picture (this would
show up as momentary white dots). You can't make the tape wait, so the front door
addresses are the ones to use when saving or loading the television picture.
(Although in practice the tape goes slowly enough for it not to matter much.)
The back door addresses are 9216 to 10239. Here you don't always get immediate
service, because the computer may be busy on the television picture. This ensures
that if you read or write using the back door addresses, you don't get any white dots.
The character set memory, where all the dot patterns are stored, has a similar
system. The front door addresses are 10240 to 11263, and the back door addresses
(which we used in Chapter 12) are 11264 to 12287.
2. One useful bytes file to save is the character set memory, so that you can load
your redesigned characters straight in from tape. Unfortunately, as we mentioned in
Chapter 12, you can't read this memory, so you can't save it directly.
The only way round this is to set up somewhere else in memory (the television
screen area will do) the bytes that are needed for the redesigned characters. You can
then save the bytes from this other area, and later load them back into the character
set memory.
3. Here's some cunning trickery. Type in your favourite word (FLATTER-ME) and
then type, all in one bufferful,
8896 32 BSAVE FLATTER LOAD CREEP FLATTER-ME
Have the tape recorder recording when you press ENTER, so that you save a bytes
file FLATTER. 8896 is the address of the input buffer when it is two lines long, so you
are saving the top line of it.
When the noise stops then you have saved the bytes file FLATTER and the
computer is looking for a dictionary file called CREEP, just after it. Press space to
BREAK, and save the dictionary with
SAVE CREEP
Now you have a bytes file FLATTER, which stores one line of the input buffer
87
CHAPTER 14
saying 'LOAD CREEP FLATTER-ME', followed by a dictionary file CREEP. Load back
FLATTER, with
8928 0 BLOAD FLATTER
(Note that the address has changed, because the input buffer is now only one line
l° n g.(
The computer will load FLATTER into the input buffer, and then, of its own accord,
load the dictionary CREEP and execute FLATTER-ME.
88
Chapter 15
FRACTIONS AND DECIMAL POINTS
All the numbers you have used so far in FORTH have been integers (i.e. whole
numbers) and in many versions of FORTH you aren't allowed any other sort. In fact it
is surprising how far you can get with integers alone, but just for convenience the
Ace also allows you to use decimal fractions. Numbers like this are called floating
point numbers (although the reasons for this are rather technical).
Here is an example of some floating point arithmetic:
2.1 2.1 F+ F.
(The decimal point here is just a full stop.)
There is a most important rule to remember here: you must always tell the
computer that it is dealing with floating point numbers and not integers. To do this,
you must use the floating point words F+ and F. instead of + and .. (Just for
comparison, try
2.1 2.1 + .
to see what happens. The answer, 16673, doesn't really mean anything.)
There are also floating point versions of —, *, / and NEGATE, and they are all
formed with an F in front: F—, F*, F / and FNEGATE. They are all written after their
operands in the usual FORTH way.
All floating point numbers in Ace FORTH are written with decimal points, and this
applies even to integers when floating point arithmetic is to be done on them.
Suppose, for instance, you want to divide 11 by 4 to get the correct answer of 2.75
instead of the integer answer, 2. You must use the floating point division, FI, and so
the numbers 11 and 4 must also be made floating point by having decimal points in
them:
11. 4. FI F.
There is an extra possibility when writing floating point numbers, which is that you
can use what is often called scientific notation. Immediately after a floating point
number (with a decimal point as usual) you can write E and an integer. This integer is
called an exponent, and indicates that the floating point number is multiplied by 10
89
CHAPTER 15
the number of times shown by the exponent. For instance,
2.1 E0=2.1
2.1 El =2.1 *10=21.
2.1 E2=2.1 *10*10=210.
2.1 E3=2.1 *10*10*10=2100.
If the exponent is negative then the floating point number is instead divided by 10:
2.1 E-1 =2.1/10=.21
2.1 E-2=2.1/10/10=.021
2.1 E-3=2.1/10/10/10=.0021
Another way of looking at the exponent is to imagine it shifting the decimal point
along some number of places.
There are two more words associated with floating point numbers, and they are
used for converting between floating point and integers.
INT converts from floating point to integer, dropping anything after the decimal point.
For instance,
12.99 INT .
- 12.99 INT .
gives 12 and -12.
UFLOAT converts from integer to floating point, so that
12 UFLOAT F.
gives 12. A quirk with UFLOAT is the way it works with negative numbers: it adds on
65536 before floating them. (This will make more sense after Chapter 17.) As a
consequence of this, the result is never negative. This explains the 'U' in UFLOAT (it
stands for 'unsigned').
Note -- There are limits on the size of floating point numbers that the computer
can handle. Considering just positive numbers for the moment, they must lie
between 1.0E-64 and 9.99999E62. If you go outside this range, the
calculations might look all right, but they are liable to give the wrong answer.
Similarly, negative numbers must lie between -9.99999E62 and -1.0E-64.
INT will only give the right answer if it is in the normal range for integers, i.e.
-32768 to 32767.
If you want to rearrange floating point operands on the stack, you need to
remember that each floating point number takes up the space of two integers. For
90
FRACTIONS AND DECIMAL POINTS
instance, to drop a floating point number from the stack, you need to do an ordinary,
integer, DROP twice.
You may like to define your own floating point stack rearrangement words. Here
are some definitions of the common ones.
:2DROP
(f.p. no. — )
DROP DROP
9
: 2DUP
(f.p. no. — f.p.no., f.p. no.)
OVER OVER
: 2SWAP
(fpl.fp2-fp2.fpl)
4 ROLL 4 ROLL
: 20VER
(fpl.fp2-fpl.fp2.fpl)
4 PICK 4 PICK
: 2 ROT
(fpl.fp2.fp3 - fp2.fp3.fpl)
6 ROLL 6 ROLL
: 2 @
(address — fp)
DUP @ SWAP 2+ @
9
: 2!
(fp, address —)
ROT OVER ! 2+ !
Summary
Floating point numbers
FORTH words: F+, F-, F*. FI, FNEGATE, INT, UFLOAT
Exercises
1. Define 2ROLL and 2PICK, floating point versions of ROLL and PICK.
91
CHAPTER 15
2. Which of these are sensible?
2 3 F/ F.
2 7.6 F+ F.
2 2.5 F*
23 + .
(Answer — none of them. Work out what they ought to be.)
3. The techniques of floating point arithmetic fill many big books, so I'll just show you
an example or two.
Square roots: The square of a number is the number multiplied by itself; taking a
square root is the reverse process. You want to find the number whose square is
some given number. For instance, 16 is the square of 4, so 4 is the square root of 16.
Usually you cant calculate square roots exactly, because the answer is an infinite
decimal. The square root of 2, for instance, is approximately 1.41421.
This word SQRT calculates the square root of a floating point number.
: SQRT
(floating point no. — square root)
1.100
DO
20VER 20VER FI F+
5 F*
LOOP
2SWAP 2DROP
This uses what is called the Newton-Raphson method. You start off with a very
rough approximation (1.) to the square root, and then each time round DO LOOP
refines it to a better approximation.
What happens if you do -2. SQRT ? Every square is positive, so in fact -2. can't
possibly have a square root. Nonetheless, SQRT still tries giving a nonsense answer.
4. Here is a word to calculate the sine of an angle, expressed in radians. It is
accurate to three decimal places for angles around 2 n, and five decimal places for
angles around tt or less. It sums the terms in the power series
3 5 7
XX X
sin x = x-+-+ . . .
3*2 5 * 4 * 3*2 7* 6 *5 *4*3*2
: SIN
(x - sine of x)
2DUP2DUP2DUP F* FNEGATE
2ROT 2ROT ( -x*x, x, x)
27 2
92
FRACTIONS AND DECIMAL POINTS
DO
( —x*x, sum so far term in power series)
6 PICK 6 PICK ( copy — x*x to top)
F* I I 1+ *
UFLOAT FI ( —x*x, sum next term)
2DUP 2ROT F+ 2SWAP ( —x*x, next sum, next terr)
2
+LOOP
2DROP 2SWAP 2DROP
Here are words for the cosine and tangent.
: COS
(x — cosx)
1.57080
2SWAP F- ( pi/2—x)
SIN
: TAN
(x tan x)
2DUPSIN
2SWAP COS FI
93
Chapter 16
READING THE KEYBOARD
From what you've seen so far, the only control you have over how a word executes is
through the stack: you can type in the operands before the word itself. Here now are
some ways in which the word can get information either direct from the keyboard or
from what you type in after the word.
The easiest way is with a word INKEY that simply reads the keyboard. If you are
pressing a key (it takes the shifts into account too) then INKEY leaves its ASCII code
on the stack; otherwise, or if you're pressing several keys, it leaves 0 on the stack.
(— ASCII code).
There are many different ways of using INKEY. If you have a program to play a fast
game, then you might want to move something on the screen if the player is pressing
5, 6, 7 or 8 (the keys with arrows), and otherwise leave it where it is to be shot at. If
your program is more relaxed you might simply want to wait for the user to press any
key, as with this word WAIT:
: WAIT
(-)
BEGIN
INKEY
UNTIL
Or you might want to wait for the user to press a key, and leave its ASCII code on
the stack:
: KEY
( — ASCII value)
BEGIN
INKEY ?DUP
UNTIL
Here is a more elaborate word. It prints "(Y/N)?" and waits for the user to press 'y'
or 'n'.
It leaves 1 on the stack for 'y', 0 for 'n'.
: Y/N
( — Oorl)
94
READING THE KEYBOARD
(Y/N)?"
BEGIN
INKEY DUP ASCII y =
IF
2
ELSE
ASCII n =
THEN
?DUP
UNTIL
1 -
INKEY reads straight from the keyboard, so it is only good for reading one
character at a time. There are also a number of words that play with the input buffer,
thus giving the program a chance to deal with entire words that you type in.
First, there are two words that stop the program and allow you to type anything
you like into the input buffer, using the cursor movements and so on in the usual way
if you need to. When you press ENTER the program continues, presumably to
analyse your typing.
QUERY clears out the input buffer first, and then lets you type.
RETYPE doesn't clear out the input buffer, so you have a chance to edit what was
there. The cursor is initially Q, meaning, 'Do you want to change any of this?'.
Neither of these affects the stack in any way.
Next, there are four words to deal with the input buffer once you have typed into it.
The simplest is LINE, which just interprets the input buffer as numbers and FORTH
words in the usual way. It even gives you Q for an unrecognised word, so it acts
exactly like the usual process of interpreting a line. However, when it has finished the
line, it continues with your program. Its effect on the stack depends entirely on what
happens to be in the input buffer.
Here is a word INPUT that provides a very simple means of letting a program stop
to allow you to type in a number. (If you know BASIC you've probably been waiting
for this.)
: INPUT
(-?)
QUERY LINE
This is extremely flexible, because it allows you to do calculations to produce your
number - for instance if the number you want to type in is 32*23 but you can't be
bothered to multiply this out yourself, for INPUT you can just type in
32 23*
However, it also puts a lot of trust in the users, because they can easily type in
95
CHAPTER 16
something that leaves more than one number on the stack, and that would probably
lead your program astray.
Here is a modified version of INPUT that, while not foolproof, provides a pretty
good measure of protection. It plants -32768 on the stack and checks that it is still
there afterwards.
: INPUT
(-?)
-32768 QUERY INVIS LINE VIS SWAP -32768 —
IF
Hello, hello, hello, what's"
CR going on here, then?"
QUIT
THEN
We use INVIS and VIS here because normally LINE copies the input buffer to the
upper part of the screen in the usual way (although it doesn't print OK). You'd
probably want to suppress this when a program is running.
Note the word QUIT here when an error is found. QUIT jumps out of the entire
program not only INPUT, but all the words currently being executed and lands
you back in the usual command state where you type in numbers and words to be
executed. Note that the QUERY LINE in INPUT manages to imitate this command
state, but QUIT bypasses this and goes right back to the true command state. QUIT
doesn't clear the stack. There is a word ABORT that is much the same as QUIT,
except that it does clear the stack.
You can get more control over the input buffer by analysing it word by word: there
are three words to do this, namely NUMBER, FIND and WORD- Each, if it finds what
it is looking for, will take it from the beginning of the input buffer and (unless invisible
mode has been set with INVIS) copy it up to the upper part of the screen.
NUMBER seeks a number (integer or floating point) at the start of the input buffer.
If there isn't one, it simply leaves 0 on the stack. If there is, NUMBER copies it up to
the upper part of the screen, and leaves its value on the stack together with (on top) a
code number to distinguish between integers (code number=4102) and floating
point numbers (code=4181).
Here is an example, a word INTEGER to get an integer from the input buffer. If the
input buffer does not start with an integer, then a retype is offered.
: INTEGER
(— integer)
BEGIN
RETYPE NUMBER DUP 4181 =
IF
(floating point number)
ignored "
96
READING THE KEYBOARD
DROP DROP DROP 0
THEN
UNTIL
The next word, FIND, is used for finding words that are defined in the dictionary.
Each word definition occupies some space in memory, and has an address
(technically known as the compilation address) to show whereabouts in memory the
definition is. When FIND finds a defined word at the start of the input buffer, it copies
it up and leaves its compilation address on the stack. For instance, type in
FIND DUP .
and it will print the compilation address of DUP.
Notice the way things are taken out of the input buffer one at a time and copied up:
1. The Ace is looking for words to execute, and the first one is FIND. It copies it
up, and at this point the input buffer contains
DUP .
2. Now the Ace executes FIND. It again looks for a word, this time so that it can
leave the compilation address on the stack. It finds DUP and copies it up, leaving just
in the input buffer.
3. Having executed FIND, the Ace starts looking for another word to execute. It
finds ., copies it up (leaving an empty input buffer) and executes it, printing the
compilation address of DUP.
4. Having executed . the Ace starts looking for another word to execute. There are
none, because the input buffer is empty, so it stops, prints OK, and waits for you to
type more.
Note how each word is taken out of the buffer when it is used. Even in invisible
mode this happens, although they are not then copied up to the top.
A word that makes use of compilation addresses is EXECUTE (compilation
address —). It takes a compilation address off the stack and executes the
corresponding word. Bear in mind that if you use REDEFINE some of the compilation
addresses many change.
The next word is WORD, which is useful even for nonsense words that are neither
numbers nor defined in the dictionary. Knowing that spaces are never part of a word
but are used to separate words off from each other, it locates the first word in the
input buffer. It copies the word to the top of the screen as usual, but also copies it to
the beginning of an area of workspace known as the pad, first clearing the pad out
with spaces. The pad consists of 254 bytes of RAM memory, starting at address
9985, and is used as an area of workspace for dealing with text. The word PAD puts
its starting address, 9985, on the stack.
97
CHAPTER 16
Let us look more closely at what WORD does.
First, it clears the pad out with spaces.
Second, it takes an operand off the stack, which is the ASCII code for the delimiter
to be used. Although we said that spaces were used to separate words, which
means that the space character is the delimiter, this role could be played by any other
character. Whether you use a space or some other character, you must leave its
ASCII code on the stack for WORD. Remember that the space has ASCII code 32; for
other characters you'd use ASCII.
Third, WORD locates a word at the start of the input buffer. It ignores delimiters
before the word, and reads it up to either a delimiter or the end of the buffer.
Fourth, it takes the word out of the input buffer and copies it to the top of the
screen (if in visible model and to the pad, starting at address 9986. This copying
includes the delimiter (or a 0 if the word was at the end of the line). It puts the length
of the word (not including the delimiter) into the very first byte of the pad, at address
9985.
Fifth, it stacks the address 9985 of the pad.
Suppose for example you type in
32 WORD axolotl .
Naturally you haven't defined a word AXOLOTL, but WORD doesn't care about
that. 9985 will be printed out by ..
The first few bytes of pad are now.
Address
9985
7
length of 'axolotl'
9986
97
ASCII code for a
9987
120
ASCII code for x
9988
111
ASCII code for o
9989
108
ASCII code for 1
9990
111
ASCII code for o
9991
116
ASCII code for t
9992
108
ASCII code for 1
9993
32
ASCII code for space
You can check this for yourself with C@ and ..
Also try
9986 7 TYPE
to; see the word 'axolotl' printed out
98
READING THE KEYBOARD
When text is stored in memory, we need to know where it is and how long it is. We
have here two ways of specifying this.
1 TYPE uses the address and length explicitly, and takes them from the stack.
2. When WORD sets up the text (in the pad) it precedes it with a byte containing
the length. All it needs to leave on the stack is the address.
Many versions of FORTH have a word COUNT that converts the address of
method 2 into the address and length of method 1 : (address address+1, length).
Here is how to define it for yourself.
:COUNT
DUP 1 + SWAP C@
Here is an example that uses WORD and COUNT. It takes a message that you
type in, and moves it across the middle of the screen over and over again.
: MESSAGE
(-)
ASCII ~ WORD CLS
BEGIN
32 0
DO
10 I AT SPACE DUP
COUNT TYPE ( Print message one place to right)
11 0 AT 32 SPACES ( Erase any part that
spilled over to next line)
1500 0
DO
LOOP
LOOP
0
UNTIL
We've used (symbol shifted A) as the delimiter for WORD, so that you can put
spaces in the message, e.g.
MESSAGE Hello there! ~
If you miss out the ~ then the spaces at the end of the input buffer will count as
part of the message.
Remember that it doesn't matter whether the input buffer is left over from your
original typing (as in the example with MESSAGE) or the program stops to let you
type more in (as in INTEGER): LINE, NUMBER, FIND and WORD still act on it in the
same way.
99
CHAPTER 16
Summary
The pad and the input buffer
Forth words: INKEY, QUERY, RETYPE, LINE, QUIT, ABORT, NUMBER, FIND,
EXECUTE, WORD, PAD
Exercises
1. Many versions of FORTH contain a word — TRAILING ( address, length with
spaces — address, length without spaces) that starts off with the address and length
of some text (as produced by COUNT and used by TYPE) and changes the length to
exclude any spaces at the end of the text. Write a definition of — TRAILING. Make
sure it works if the text is all spaces.
2. Define a word
: PCT
PAD COUNT TYPE
9
You can easily use PCT to see what's at the start of the pad. Investigate what the
following words do to the pad: ASCII, SAVE, LOAD, They all have some effect on
it, so text stored there by WORD is not safe forever., also uses the pad, but at the
other end.
3. Since the pad is only 254 bytes long, there is a limit to how long a word WORD
can pick up. WORD will take at most 253 characters from the input buffer; if there are
any left over in the word then the byte at the start of the pad will show 254 (even
though only 253 characters have been taken). Try this out, using 32 WORD and long
strings of characters.
4. Some time when the computer is empty, define
:FOREVER
BEGIN
QUERY 0
UNTIL
This is very difficult to BREAK, because you need to press ENTER to get out of
QUERY, and then press BREAK before it gets back into QUERY again. You can
sometimes do it by keeping SHIFT down and pressing ENTER and SPACE almost
simultaneously, but it's not easy. The best cure is not to write a word like FOREVER.
100
Chapter 17
OTHER WAYS OF COUNTING
In English, as in most languages, counting proceeds in blocks of ten: after a bit of
initial wavering, it settles down as
twenty, twenty-one, ... , twenty-nine
thirty, thirty-one, ... , thirty-nine
and so on.
This grouping into tens is reflected even more rigidly in the usual way of writing
numbers, with ten digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9.
Mathematically speaking, ten is nothing special as a number and we use it simply
because humans have ten fingers each .-. in fact digit really just means finger.
Penguins would never dream of counting in tens, because they have two flippers
instead of ten fingers. They count in twos.
How do they make this work? First, instead of our ten digits 0 to 9, they have just
two penguin digits, 0 and 1. They also call them bits instead of digits. When a penguin
starts counting it manages 0 and 1 all right, but then gets stuck because there are no
more bits for it to use. We would go on to 2 because we've got eight more digits
available to take us up to 9, but then we get stuck in the same way.
Our answer is to start using pairs of digits instead of single ones: after 9 comes 10,
meaning one ten and no units. The penguin answer is exactly the same, except that
they have to start using it much earlier: having run out of bits with one, they write
two as 10, and three as 11. Then they get stuck again, so they apply the same
process a further stage and write four as 100.
Number
Human digits
Penguin bits
Nought
0
0
One
1
1
Two
2
10
Three
3
11
Four
4
100
Five
5
101
Six
6
110
Seven
7
111
Eight
8
1000
Nine
9
1001
Ten
10
1010
Eleven
11
1011
Twelve
12
1100
101
Chapter 17
Thirteen
13
1101
Fourteen
14
1110
Fifteen
15
1111
Sixteen
16
10000
If you want to pretend to be a penguin, type
2 BASE C!
so that you can add 10 and 10 to get 100:
10 10 + .
BASE is a variable provided by the computer itself, a system variable. However,
unlike ordinary variables its value is just one byte and you must use C@ and C! with it
instead of @ and !. Its value is the number base you are currently using, or how many
fingers the computer thinks you have. Having set BASE to two, you must type
integers in using the penguin notation. (Floating point numbers are different - they
are always in base ten except for the exponent part which uses BASE.) The
computer will also print the numbers out in penguin.
To get back to human notation, type
1010 BASE C!
because 1010 is penguin for ten. A useful word here, which would have done the
same, is DECIMAL: DECIMAL sets BASE to ten. Decimal here means 'based on
tens', as in 'decimal coinage', so our notation is a decimal system. The penguin
notation, based on twos, is a binary system.
The Jupiter Ace is designed for use by many different species, not just humans and
penguins, and you can set BASE accordingly. A three-toed sloth, counting on one
foot while it hangs on to a branch with the other three, would set BASE to three; a
fork would set BASE to four; and a one-armed bandit would set BASE to five.
Animals with more than ten fingers have a different sort of problem when they
reach ten. Although they ve used up our decimal digits, they need some more of their
own before they can get on to their 10. The rule is to start using letters, like the
sixteen-fingered typist from the moon Ganymede that we employ at Jupiter
headquarters. She starts off with our digits 0 to 9, and then uses the letters A to F for
ten to fifteen. Only at sixteen does she need to write 10, and then she carries on to
19 (twenty-five), 1A (twenty-six), then to IF (thirty-one), 20 (thirty-two) and so on.
This system, with base sixteen, is called hexadecimal or hex for short.
To summarise then, you need as many digits as you have fingers. If you have more
than ten then you'll need not only our usual ten decimal digits, but a few letters as
well.
The importance of this lies in the fact that most computers are just like penguins.
They store numbers using electrical voltages that can have one of two levels (low-0,
102
OTHER WAYS OF COUNTING
high=1), or electronic switches that can be either off (0) or on (1), so they use the
penguins' binary system.
This is why powers of two crop up so often with the Ace. A byte, for instance, is a
number between 0 and 255, and this is precisely the kind of number that can be
written down with just eight bits:
the smallest byte is nought=00000000 in binary,
the biggest is two hundred and fifty-five=11111111 in binary.
How do we work out what a binary number means? In decimal notation the
different columns represent different values:
-i l 1 l 1
2 5 5
so 255 means two hundreds + five tens + five units.
In binary the same principle applies, except that the columns represent
powers of two instead of powers of ten:
11111111
Thus in binary, 11111111 means
Decimal
one * one hundred and twenty-eight
+ one * sixty-four
+ one * thirty-two
+ one * sixteen
+ one * eight
+ one * four
+ one * two
+ one * one
= two hundred and fifty five
A quicker way is to imagine adding 1 to it, which will give the binary number
1 0000 0000. The 1 is in the two hundred and fifty-sixes column, so 11111111
is one less than two hundred and fifty-six.
128
64
32
16
8
4
2
1
255
103
CHAPTER 17
An ordinary integer on the Ace is coded into two bytes or sixteen bits, so on the
face of i t you'd think integers ranged from 0 to binary 1111 1111 1111 1111 (which is
65535 in decimal - you might remember this as the largest possible address in
memory). However, we also need a way of storing negative numbers, so for this we
use the rule:
A negative number is stored in the computer with 65536 added to it.
Suppose then our integers are between -32768 and 32767, which is what we said
before. 0 and the positive numbers, 1 to 32767, are stored just as they are. The
negative numbers -32768 to -1 are stored as the numbers 32768 to 65535, so they
start where the positive numbers leave off and carry on up to the largest possible
number that can be stored in two bytes.
This method of storing negative numbers is known as twos complement form.
From this we see that two bytes stored in the computer can be interpreted in two
different ways: either as a signed number, between -32768 and 32767, or as an
unsigned number, between 0 and 65535. Which interpretation we use will often
depend on the circumstances.
For instance, you have already seen . which prints out a number. There is also a
word U. (standing for 'unsigned dot') which prints out the same number, but
interpreted as an unsigned number. If you type in -1 then it is stored as
-1 +65536=65535 on the stack, but you don't usually notice this, because . decodes
it back into the negative number -1. U., however, prints out 65535. This means that
different words can interpret the same number in different ways. Try also typing in
65535 and printing it out using ..
Another word that works on unsigned integers is U< (integer, integer flag). This
is just like < except for using unsigned integers - try
1 -1 < .
and
1 -1 U< .
U< treats -1 as 65535, which is greater than 1.
Finally, here is a useful shorthand for writing binary numbers: take a number written
in binary and separate the bits into groups of 4, starting at the right-hand end.
If the leftmost group has less than four bits, then put some Os in front of it. Next,
replace these groups by digits and letters according to this table:
Group of bits
0000
0001
0010
0011
0100
Replace by
0
1
2
3
4
104
OTHER WAYS OF COUNTING
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F
For instance, a hundred, which in binary is 110 0100, is replaced by 64.
This handy rule turns out to be exactly the same as writing the number in hex (base
16), and it works because 16=2 3 4 =2*2*2*2.
Similarly, if you divide the bits into groups of 3 then you end up using octal (base 8)
and this too is often used with computers.
Summary
Number bases -- binary, octal, decimal, hex (hexadecimal)
Signed and unsigned numbers
FORTH words: BASE, DECIMAL, U., U<.
Exercises
1. Why is binary useful when you redesign graphics characters?
2. Try
BASE C@ .
Charge the number base, and try again. Why is the number base always 10
however much you change it?
Write a word .BASE that prints out the number base in decimal. Make sure the
number base is the same after .BASE as it was before.
3. Set the number base to 36. Then the digits to use are the ten decimal digits and
the twenty-six letters. Any undefined word with three letters or digits gets stored as
a number, so you can say things like
CAT DOG . .
4. The rule for negating an integer was to subtract it from 65536. In binary, there is a
very easy way of doing this: subtract from 65535 and then add 1. This is easy
because in binary 65535 is all Is and subtracting another number from that is the
same as changing 0s to Is and Is to 0s (1-0=1, 1 - 1=0)
105
CHAPTER 17
This first step is called taking the ones complement; when you have done the
second step of adding 1, you have taken the twos complement.
5. If you use hex a lot, define a word
: HEX
16 BASE C!
6. Work out 2 10 (the answer is 1024). Because this is so close to 1000, it is often
used as a kind of binary equivalent of 1000 and is called a K. K here stands for Kilo,
because it is a bigger version of the small k in kilometre or kilogram.
It is very useful for working out approximately how big powers of two are. For
instance,
2 11 = 2*2 10 = 2K = about 2000 (actually 2048)
2 2o = 2 io * 2 io = about a mi || ion (actually 1048576)
106
Chapter 18
BOOLEAN OPERATIONS
In Chapter 9 we saw how IF interpreted numbers as conditions, being either false
(zero) or true (non-zero). There are some operations - AND, OR and XOR - that
combine conditions, although you need to make sure that the conditions are proper
flags, either 0 or 1. These are called Boolean operations, after the mathematician
George Boole.
Suppose within a word definition you have two conditions, and you want to do
something or other if they are both true - i.e. if the first condition is true and the
second condition is true. You would use AND for this. For instance, the top of the
stack is between 10 and 20 if it is greater than 9 and less than 21. You would first get
the results of these two conditions on top of the stack by
DUP 9 > SWAP 21 <
Now the stack has two flags on top. AND will replace them by 1 if they are both 1,
i.e. if the top is 1 and the second from the top is 1. Otherwise AND will replace them
by 0. After AND, you can use IF and whatever it is you wanted to do.
We can describe this action of AND with a truth table in which, given the two
operands, we can look up the result.
Top of stack
0
1
Second from top 0
0
0
1
0
1
You use OR when you want to perform your action if either the first condition is
true or the second is true (or implicitly, if both are true). Note that this last part, that if
both conditions are true so is the result of OR, is not always the way English works.
Suppose you also own one of the JOKER computers made by our rivals Saturn of
Cambridge Ltd, and it breaks down. When you send it back, they offer you either the
same computer (unmended) or a cheaper one in exchange; but if you say,. 'Fair
enough, I'll take both', they're more likely to fill your mouth in with concrete.
107
CHAPTER 18
Here is the truth table for OR.
Top of stack
0
1
Second from top 0
0
1
1
1
1
The last Boolean operation is XOR, which stands for 'exclusive OR'. This is like OR,
except that it doesn't contain the 'or both' part. In other words, for its result to be
true, one of the operands must be true but not both. Here is its truth table.
Top of stack
0
1
Second from top 0
0
1
1
1
0
We said that AND, OR and XOR only work properly with true and false numbers if
the true numbers are definitely 1 (and not some other non-zero value). What happens
then if the numbers on the stack take other values?
Let's take AND first. We know how AND works on 0 and 1, so this tells us how
AND could work on bits, bits being just 0 or 1. Therefore with arbitrary numbers we
can write them as rows of bits (by using binary notation) and AND together
corresponding bits to get corresponding bits in the result.
For instance, suppose the top of the stake is 106 (binary 110 1010) and the second
from the top is 201 (binary 1100 1001). Our AND calculation is then
0000 0000 0110 1010
mi mi mi mi
AND 0000 0000 1100 1001
0000 0000 0100 1000
so the result is binary 100 1000, or 72. OR and XOR work bit by bit in exactly the
same way.
Here is a rather typical example that shows AND being used in two quite different
ways. Let us define a word LETTER? that tests an ASCII code to see if it represents a
letter, putting 1 on the stack if it does and 0 if it doesn't.
The capital letters have ASCII codes between 65 and 90, and the lower case letters
have codes between 97 and 122 so here is one version (making use of ASCII).
:LETTER?
(ASCII code —flag)
DUP ASCII A 1- >
OVER ASCII Z 1+ < AND
SWAP DUP ASCII a 1- >
SWAP ASCII z1+ < AND OR
108
BOOLEAN OPERATIONS
However, there is a very slick trick that is typical of the way one can play with ASCII
codes. Each lower case letter has an ASCII code that is exactly 32 more than the
code for the corresponding capital. If you write the codes in binary, you discover that
to change from lower case to upper case, you just change one of the bits from 1 to 0,
e.g.
A has code in binary 0100 0001
a has code in binary 0110 0001
T
change this bit
We can do this with AND: we just AND the ASCII code with binary 1101 1111
(decimal 223) which leaves seven bits unaltered, but changes the crucial bit to 0.
This leads to a short cut in LETTER?: we can first convert to upper case and then
check that the code lies between 65 and 90.
: LETTER?
(ASCII code —flag)
223 AND
DUP ASCII A 1- >
SWAP ASCII Z 1+ < AND
Summary
FORTH words: AND, OR, XOR
Exercises
1. We stressed that to use AND, OR and XOR on true/false conditions, they must be
0 or 1. Actually, this is only the case for AND and XOR; for OR a true condition can be
anything non-zero. Why is this?
2. Define a word & that takes two numbers off the stack and leaves 1 if they are both
true in the non-zero sense, and 0 if one of them is false. (Use IF ... ELSE . .. THEN.)
Define words corresponding to OR and XOR in the same way as & corresponds to
AND.
3. You can think of the 223 in LETTER? as a mask that tells AND to leave certain bits
of the other operand (the ASCII code) unchanged, and to force a certain bit to 0.
Similarly, a mask can tell OR to leave certain bits unchanged and to force the rest to
1; and a mask can tell XOR to leave certain bits unchanged and to switch the rest
over, whatever they were originally.
Work all this out in detail.
-1 XOR takes the top of the stack and complements all its bits, changing 0s to Is and
Is to Is. How does this work? (Hint: what is -1 in binary?) This is the ones
complement of chapter 17, exercise 4.
109
Chapter 19
MORE ADVANCED ARITHMETIC
We have seen already that with one byte you can store 256 (=2 8 ) possible numbers,
and with two bytes you can store 256*256=65536 possible numbers (0 to 65535
unsigned, or -32768 to 32767 signed). With four bytes, you could store
256 4 =4294967295 possible numbers, so if you had a pair of ordinary two-byte
numbers you could handle much bigger numbers.
This is the principle behind double length arithmetic. Some words assume that the
two top numbers on the stack are not separate numbers, but the two halves of a
double length 4-byte number. These words are not as comprehensive as the words
for single length (2-byte) arithmetic. They aim at providing the minimum facilities you
need to define whatever extra double length arithmetic you need.
D+ ( dl, d2 — dl +d2) adds together two double length integers (which we've
written here as dl and d2) on top of the stack.
Here's a silly example silly, because you could do it much more easily with single
length arithmetic. It adds 3 to 5.
3 0 5 0 D+ ..
First, to get a double length integer on the stack you must put on two single length
integers: 3 and 0 for a double length version of 3; 5 and 0 for 5. If you write 3 out in
eight hexadecimal digits, it is
0000 | 0003
t I t
more significant less significant half
half higher up lower down
on stack on stack
Hexadecimal is useful here, because the two single length numbers you need to
work out for the stack correspond exactly to the first four hex digits (the more
significant part, which goes higher up on the stack) and the last four hex digits (the
less significant part, which goes lower down on the stack). If you have an integer
written in hex (4C83A2, say) and you want to put it on the stack as a double length
integer, then first make sure it has exactly eight hex digits. If if has more than eight
then give up, but our case has fewer so you can put 0s in front to give 004C 83A2. To
put this on the stack, set BASE to 16 and type
83A2 004C
110
MORE ADVANCED ARITHMETIC
In other words the hex digits are the same, but you type the second group of four
first.
Note — if your number is negative, then put any extra Os in front before you take the
twos complement. The twos complement uses the same method as in Chapter 17,
Exercise 4: take the ones complement by changing binary Os to I s and I s to Os or
subtracting from hex FFFFFFFF, and then add 1.
If your numbers are really single length but you need to do double length arithmetic
on them then you won't need to consider hex notation. To get the double length
version on the stack,
1. Put the single length number on the stack.
2. Put either 0 or -1 on the stack. Use 0 if the number is 0, or positive or unsigned,
use -1 if the number is negative.
Here are some more words for double length arithmetic.
D< ( dl, d2 — flag). 1 (true) if d 1 is less than d2.
DNEGATE ( d-d) negates the double length integer d.
U* ( n 1, n2 — nl *n2) does a multiplication. The two numbers nl and n2 on the
stack are unsigned single length integers. The result, their product, is also unsigned,
but it is double length. Remember that not only are the numbers here treated as
being unsigned (as suggested by the U in the name), but the answer is also double
length.
256 256 U* . .
gives 1 and 0, the two parts of the double length number 65536 (hex 10000).
U/MOD ( dl , n2 remainder d1/n2, quotient). As suggested by the U in the name,
this a version of /MOD in which all the numbers involved are treated as being
unsigned. As in U*, there is some double length arithmetic involved as well: the
dividend dl (the number you divide into) is double length. All the other numbers are
single length
*/ ( nl, n2, n3 — (nl * n2)/n3) You have seen this before, in Chapter 5. I can now
explain that while */ is calculating it works out nl * n2 as a double length number. This
preserves accuracy, even though 'you never see any double length numbers on the
stack, */MOD is similar.
We now go on to see some ways of controlling how a number gets printed out, i.e.
of formatting the number. This is defined to work on double length integers.
However, you will also find it useful on single length integers so remember the rules
above for putting 0 or -1 on the stack to convert single length to double length.
The problem with outputting numbers is that the best way is to calculate the digits
in the wrong order To; output 123 you can easily work out the 3 -. divide 123 by 10,
giving 12 remainder 3, this remainder is the last digit. When we repeat the
111
CHAPTER 19
process, dividing 12 by 10, we get the next to last digit, 2; and finally we get the first
digit 1. This method works with any number base.
FORTH provides a method of storing these digits backwards in the pad until they
are all there.
<# starts this off.
#> finishes it by dropping a double length integer from the stack and leaving the
address of the first digit and the number of digits (ready for TYPE).
In between you use # and #S to produce the digits.
# ( dl — d2) produces one digit out of an unsigned double length integer on the
stack. It divides it by the current number base (in BASE), leaves the (double length)
quotient on the stack, and uses the remainder to put a digit in the pad.
#S ( dl 0 — 0,0) is # repeated until the double length integer on the stack is 0. In
other words, it produces all the digits. Note that if the original double length integer
dl is 0, then #S produces one 0 digit.
All that # and #S can store in the pad are the digits, but you can also put in any
other character you like using HOLD
HOLD ( ASCII code —) stores a character (with the given ASCII code) in the pad in
the same way as # stores a digit.
Here is a word MONEY to print out a sum of money that is stored on the stack as
the number of pence, a single length integer.
:MONEY
(pence -)
0 ( make double length)
<# # # (two digits for pence)
ASCII . HOLD ( decimal point)
#S ( pounds)
ASCII £ HOLD #> TYPE
>
Note how everything goes backwards between <# and #>.
Here s another example, which shows that you can do calculations within
<#...#> but you must be careful to remember whether you've got single- or
double-length Integers) It takes a time in seconds, and prints it as
hours minutes seconds
112
MORE ADVANCED ARITHMETIC
: MINSEC
( divides top of stack by 60 and stores in pad two digits
from remainder followed by a colon)
( n — n/60)
60 /MOD SWAP
0 # # DROP DROP
ASCII : HOLD
: TIME
( no. seconds —)
<# MINSEC ( :seconds)
MINSEC ( :minutes)
0 #S( hours)
#> TYPE
J
The last word connected with <# and #> is SIGN ( integer —). SIGN takes a
single length signed integer off the stack and, if it is negative, HOLDs a minus sign.
Remember that # uses unsigned numbers, so if you want to print signed numbers
you must apply # to the absolute value, and use SIGN at some point. Here, for
instance, is a word D. to print a signed double length integer.
: D- >PAD
( double length integer—)
DUP >R DUP 0<
IF
DNEGATE
THEN
<# #S R> SIGN #>
: D.
( double length integer—)
D->PAD TYPE
Note that the sign of a double length integer is shown by the 32nd (most
significant) of its 32 bits (1 for negative, 0 for positive or zero), and this is the same as
the sign of the single length integer that forms the more significant part (2 bytes) of
the double length integer. In D- >PAD, we start off with the two parts of the double
length integer on the stack:
(less significant part, more significant part)
DUP >R stores the more significant part on the return stack to remember the sign.
113
CHAPTER 19
We still have the signed double length number on top of the stack, and our next
step is to negate it (with DNEGATE) if it is negative. Then we can use #S to hold the
digits of the absolute value, and R> SIGN to hold the sign.
You can now see double length arithmetic working more clearly - try things like
256 256 U* D.
Here are some definitions of double length versions of single length arithmetic
words. How do they work?
: D0=
(d — flag)
OR 0=
: D0<
(d — flag)
SWAP DROP 0<
: DABS
( d — absolute value of d)
DUP 0<
IF
DNEGATE
THEN
Try writing more of your own. You can use 2DROP and so on from Chapter 15 for
double length integers.
A sort of opposite of formatted output is CONVERT, which converts text into a
double length integer. The general idea can be shown by seeing how the characters
"123" can be converted into a number, using base 10. We use an accumulator,
initially zero, to store the number we have read up to: at each stage we multiply the
accumulator by 10 and add on the next digit. We shall write the accumulator in
English just to make clear the distinction between the text "123" and the number
(actually stored in binary) in the accumulator.
First stage: accumulator=nought.
Second stage: read the "1". Multiply the accumulator by ten and add one, giving one.
Third stage: read the "2". The accumulator becomes one* ten + two = twelve.
Fourth stage: read the "3". The accumulator becomes a hundred and twenty-three.
This is really just the reverse of #S. You can see that you can replace "ten" by any
other number base.
CONVERT starts off with a double length accumulator on the stack - probably
nought, but maybe you've accumulated an earlier part of the number already. On top
114
MORE ADVANCED ARITHMETIC
of the accumulator is the address of the character one byte before the text you want
to read. CONVERT reads the characters of the text one by one, adjusting the
accumulator, until it finds one that isn't a digit (in the current number base). It then
stops, leaving on the stack the new accumulator and the address of this non-digit,
(accumulator, address of byte before text
- new accumulator, address of non-digit)
As an example, here is a word CONVERT, that reads in a double length integer
starting at a given address, ignoring commas,
: CONVERT,
( address - double length integer, address of terminator)
1-0 0 ROT
BEGIN
CONVERT DUP C@ ASCII , -
UNTIL
Finally in this rather mixed chapter I shall describe how floating point numbers are
set up. Like a double length integer, a floating point number uses four bytes, but in a
quite different way. Any floating point number can be written in the form
.xxxxxxEy
in scientific notation, with a decimal point, 6 decimal digits xxxxxx (the first one not
0), and an exponent part y. Thus 123.456 would be written .123456E3, and .0001234
would be written .123400E-3.
The Ace uses three of the four bytes in a floating point number to store the six
digits, since four bits can store a number between 0 and 15 (a hex digit), they can
easily store a decimal digit, so a byte can store two decimal digits, one in each of the
groups of four bits. (This is known as binary coded decimal, because the number is
not converted fully into binary: only its decimal digits are.)
You can see this by changing the number base to 16, and typing
123.456 U. U.
43| 12 3456
^ Y !
exponent binary coded decimal
byte digits
Note that when 123.456 is on the stack, it occupies two ordinary stack entries. The
one lower down contains the last four binary coded decimal digits (3456), and the one
higher up contains the first two digits (12) and the exponent byte. The possible
115
CHAPTER 19
exponents range from — 63 to 63, so they can be stored as binary in seven bits. For
technical reasons, they are not stored by the usual method for storing signed
integers; they are made positive by having 64 added to them. This offset exponent
then lies in the range 1 to 127 and can be stored in seven bits.
There is one remaining bit in the exponent byte, and that shows the sign of the
entire floating point number: 1 for negative, 0 for positive. This is in the same place as
the bit that shows the sign of a double length integer, so you can use D0< for floating
point numbers too (i.e. as an F0< ).
The floating point number 0. is special. All four of its bytes are zero. This means
you can also use D0= for floating point numbers (as an F0=).
Summary
Double length integers
Formatted output
Floating point numbers
FORTH words: D+, D<, DNEGATE, U*, U/MOD, */, <#, #, #S, #>, HOLD,
SIGN, CONVERT
Exercises
1. Write a version of MONEY that always prints three digits for the number of
pounds, using * characters on the left if the number of pounds itself hasn't got
enough digits (e.g. print £89.95 as £*89.95).
2. Try defining these double length versions of words you know already: D-, D=,
DMAX, DMIN and DU<.
Another word available in some implementations of FORTH is D.R.
D.R ( d, n —). Here d is a double length integer to be printed and n is the number of
characters it is to print (with spaces used on the left to fill up any unused room). Here
is a definition of D.R, using the D->PAD we defined for D..
: D.R
(d, n —)
>R D->PAD
R> OVER - SPACES TYPE
Note that if the number is too big to fit in the n places, then it runs over at the end.
Define a word S->D to convert a signed single length integer to a double length
one.
116
Chapter 20
INSIDE THE DICTIONARY
As you know, when a word is defined, its definition is put in the dictionary. So far
you've seen three different kinds of words, defined by :, CONSTANT and
VARIABLE, and although these three may seem very different, in fact there are
certain general principles that apply to all words.
1. Every word has a name.
2. Every word contains some information specifying (a link to) the previously
defined word. This means that all the words are linked together in a long chain, each
one saying where the next one is. The chain starts at the newest word and works its
way (in the order you see in VLIST) to the oldest, which has a code showing that it is
linked to nothing.
3. Every word has some information (a 2 byte code called the code field) showing
generally what is to be done when the word is executed: e.g. stack a number (for
CONSTANT), start executing some more FORTH words (for:).
4. Almost every word has some more information (its parameters) that is like the
code field but more specific: e.g. the number to be stacked (for CONSTANT), or the
FORTH words to be executed (for:).
The name, link and code field have the same format for every conceivable kind of
word, so these are grouped together into the header of the word. Every word has a
header.
The parameters are very variable in format; they could be anything from a single
number to a long FORTH program. How they are used depends on the code field.
They are called the parameter field.
These principles are quite general, and mean that FORTH words are not restricted
to those defined by :, CONSTANT and VARIABLE. The simplest way of making your
own sort of word is with CREATE. CREATE makes a word with a header but no
parameter field, and puts it in the dictionary.
You may imagine that its lack of parameter field makes it quite useless, but this is
not so for the following reasons. First, you can (and probably will) make up your own
parameter field for it, and second, the code field specifies that when this new word is
executed it leaves on the stack the address of its parameter field and does nothing
else. You can then pick up this address to use the parameter field in your own way.
One common way of using CREATE is to make super-variables that can store more
than one number. (These are called arrays.) For instance, you might want to store
twelve numbers that are the numbers of days in the twelve months in an ordinary
117
CHAPTER 20
year. You can set up a word MONTHS whose parameter field contains these twelve
numbers in order:
Header for MONTHS | 31, 28, 31,30, 31,30, 31, 31,30, 31,30, 31
parameter field
To set up the header and put it in the dictionary you type in
CREATE MONTHS
The next problem is to set up the parameter field, and for this you use a word
, (n —) takes a number off the stack and encloses it in the dictionary— i.e. it sets
aside two extra bytes for the dictionary, and writes the number in.
For the twelve numbers, you'd type
31 , 28,31 , 30,31 , 30 ,
31 , 31 , 30,31 , 30,31 ,
Remember that , is actually a FORTH word, not just punctuation in a list, so you
need spaces to separate it from the numbers. Also remember the , right at the end,
which is just as necessary as the others.
The word MONTHS is now fully defined. To use it, define a word MONTH to
convert a month (1 for January up to 12 for December) to a number of days.
: MONTH
( month — no. days)
1- DUP + MONTHS +
The definition of MONTH will be entered in the dictionary immediately after the final
31 in MONTHS.
MONTHS leaves on the stack the address of its parameter field, i.e. the address of
the 31 for January. We want to add on to this 0 for January, 2 for February, 4 for
March, and so on up to 22 for December (the numbers are doubled up because there
are two bytes for each month length in MONTHS). 1- DUP + converts from the
month code we're given (1 to 12) to the doubled-up month code (0 to 22). When
we've added this to the parameter field address of MONTHS, we get the address of
the month length we want and @ gets the month length itself.
Here is a similar application, to store the three-letter abbreviations (TLAs) for the
days of the week: Mon, Tue, Wed etc. We shall set up a word DAYTLAS that
contains the 21 characters necessary, and since a character only takes up one byte,
we shall use C,, a one byte version of,.
C, (number — ) encloses a number in the dictionary, like ,, but it only uses one byte
of dictionary space instead of two.
118
INSIDE THE DICTIONARY
Typing in ASCII M C, ASCII o C, etc could be a bit of a bore, so we define an
auxiliary word STRING, just to help us define DAYTLAS. It takes a word from the
input buffer, and encloses its characters in the dictionary.
: STRING,
32 WORD COUNT ( COUNT as in Chapter 16)
OVER + SWAP
DO
IC@ C,
LOOP
Now type
CREATE DAYTLAS STRING, MonTueWedThuFriSatSun
If you're not going to need STRING, again, then a cunning trick you can play is to
say
REDEFINE STRING,
which erases STRING, and replaces it with DAYTLAS.
Now you need a word .DAY that, given the number of a day in the week (1 for
Monday up to 7 for Sunday), prints out its TLA.
: .DAY
(day —)
1- 3 * DAYTLAS +
3 TYPE
Another word used in setting up parameter fields is ALLOT (no. bytes —). This sets
aside a number of bytes (specified by the top of the stack) in the dictionary for your
parameter field, in the same way as C, and , set aside one and two bytes; but unlike
C, and , it doesn't store any numbers in these bytes. It just makes the space available
as part of the parameter field.
Now for something clever. CREATE is rather simple minded in what it does. It only
sets up a header, so it doesn't give you any help in setting up the parameter field, and
when the new word is executed all the help it gives is to leave its parameter field
address on the stack. This is all very well if your word is just a one-off job, but if you
have more rather similar words you'll get bored with having to do the same work
more than once.
Suppose you want the equivalent of .DAY in several languages: .JOUR for French,
.TAG for German and so on. For each one you need
1. The data (the actual TLAs) including a way of setting it up (this is what STRING,
does), and
119
CHAPTER 20
2. A method of using the data to print out the relevant TLA. This is going to be
largely the same as .DAY.
The only thing that varies from one language to another is the data, so we are going
to define a word MAKEDAYS that contains both the method for setting up the data
and the method of using it. We shall then define .DAY by saying
MAKEDAYS .DAY MonTueWed ....
which will use MAKEDAYS' knowledge of how to set up data. Now .DAY itself
contains the data - we don't need a separate DAYTLAS. When we use .DAY (in the
same way as before), it will refer back to MAKEDAYS to find out how to use the day
number.
Here is how we do it. First, we define MAKEDAYS, not with :, but with two words
DEFINER and DOES>. These are always used together. (FORGET DAYTLAS and
.DAY.)
DEFINER MAKEDAYS
32 WORD 1+ DUP 21 + SWAP
DO
IC@ C,
LOOP
DOES>
SWAP 1- 3 * +
3 TYPE
(Note the ;, just as in a colon definition.)
This is in two parts. The first part, the defining part, goes up as far as DOES>.
When we say
MAKEDAYS .DAY MonTueWedThuFriSatSun
the first thing that MAKEDAYS does is to make a header for .DAY, just as CREATE
does - except that the code field is different, as we shall see. MAKEDAYS then
executes its defining part, to set up the parameter field for .DAY - in our case it reads
a string from the input buffer, and encloses twenty-one characters from it in the
dictionary much as STRING, did. Now .DAY is fully defined, and MAKEDAYS is
finished with for the time being.
The next part of MAKEDAYS, from DOES> to ;, is the action part. It is used when
we say something like
3 .DAY
If .DAY had been set up by CREATE, then not much would happen - .DAY would
just leave its parameter field address on the stack. However, it can do more than that
120
INSIDE THE DICTIONARY
because it has MAKEDAYS to refer back to: .DAY still leaves its parameter field
address on the stack (on top of the 3 that you typed), but it then goes through the
action part of MAKEDAYS. This uses the two numbers to print out the relevant TLA.
Type in
MAKEDAYS .JOUR lunmarmerjeuvensamdim
MAKEDAYS .TAG MonDieMitDonFreSamSon
4 .JOUR
5 .TAG
This is one of the cleverest ideas in FORTH, so it's well worth mastering.
Remember that MAKEDAYS is not just any old common or garden word; it has the
power to define new words and so is on a par with :, CONSTANT, VARIABLE and
CREATE: it is a defining word. DEFINER, of course, is even further up in the clouds,
because it defines new defining words. In the next chapter, we shall see how it can
be used to provide facilities that FORTH in its bare form lacks.
Note: If you are using a defining word (like MAKEDAYS) that was defined by
DEFINER, and while it is defining a new word (like .DAY) an ERROR crops up, then
the incomplete definition will be left in the dictionary. This won't do any damage, but
it is untidy and wastes space, so you should FORGET it. There is a way round this
explained in Chapter 24, Exercise 5.
Another note — DEFINER is done differently on other versions of FORTH. Instead
of writing
DEFINER name
you'd write or
: name CREATE
or
: name < BUILDS
The effect is the same, but these other forms are not possible on the Ace: you use
DEFINER instead.
Finally, here in detail is the format of the header of a word.
First is the name of the word, the name field. This has one byte for each character
of the name (at most 63). Letters are converted to upper case, and the last character
is shown as such by having 128 added to it (i.e. its most significant bit is changed
from 0 to 1). This would normally show that the character is inverse video, but not in
this case.
Second are two bytes, the length field, that store the total length in bytes of the
word definition, excluding the name field: 7 for the rest of the header + the length of
the parameter field. The length field is filled in when the next word is defined.
Third are two bytes for the link field, which is the address of the name length field
121
CHAPTER 20
of the last word defined before the present one.
Fourth is one byte for the name length field, the number of characters in the name.
This can have 64 added on to make the word an immediate word (see chapter 23).
Fifth are two bytes for the code field, which specify how the word is to behave.
When you use FIND, its result is the code field address of the word found, i.e. the
address of its code field. The parameter field follows immediately after the header, so
the parameter field address is always 2 more than the code field address.
Summary
Headers of words - name fields, length fields, link fields, name length fields, code
fields, parameter fields.
FORTH words - CREATE, „ C„ ALLOT, DEFINER, DOES>
Exercises
1. If you didn't already have VARIABLE and CONSTANT, how could you define
them with DEFINER? Define similar words 2VARIABLE and 2CONSTANTthat store
4-byte numbers - i.e. either floating point numbers or double length integers.
2. Define a word - HEAD, say - with CREATE, and compare
HEAD .
with
FIND HEAD .
The first one gives the parameter field address, which is 2 more than the code field
address given by the second.
3. Many versions of FORTH have a word ' (pronounced 'tick') which is just like FIND
except that it gives the parameter field address instead of the code field address. ' is
easy to define on the Ace:
FIND 2+
4. In Chapter 11, Exercise 1, we promised a quicker way of calculating pitch
numbers, given semitones. The problem was, given a number n of semitones
between 0 and 11, to multiply a fixed bass pitch number by (|)" /12 , and there we
multiplied it by (})' 12 n times. A better way is to store the twelve numbers in an array.
We store each one as a fraction, with two numbers (like 17843/18904 for (|) 1/12 ).
Define
122
INSIDE THE DICTIONARY
CREATE SCALE 1,1, 17843 , 18904 ,
26547,29798 , 16585 , 19723 ,
4813,6064,5089,6793 ,
19601 , 27720,6793 , 10178 ,
3032,4813,5377,9043 ,
14899,26547,9452,17843 ,
(24 numbers altogether).
The new version of SEMS is now
: SEMS
( semitones above middle C — pitch number)
36 + ( semitones above bottom C)
12 / MOD SWAP ( no. octaves, no. spare semitones)
3822 SWAP
DUP + DUP + SCALE +
DUP @ SWAP 2+ @ 7
SWAP ?DUP
IF
( divide by 2 for each octave)
0
DO
2 /
LOOP
THEN
5. Here is a neat way of testing .DAY, .JOUR and so on, by using FIND and
EXECUTE.
: TEST FIND 8 1
DO
I OVER EXECUTE SPACE
LOOP
DROP
To test .DAY, type in
TEST.DAY
123
Chapter 21
STRINGS AND ARRAYS
Here are two very useful examples that use DEFINER. Strings are sequences of
characters manipulated as single units, while arrays are variables that store more
than one number. Many computer languages have strings and arrays built into them.
FORTH usually doesn't, but as you will see this doesn't matter because you can
define the facilities for yourself — or buy someone's cassette tape to do the same
thing.
Strings
A string is just a sequence of characters. It is not the same as a word, because a
word has a meaning given to it by its definition in the dictionary. A string has no such
meaning. FORTH allows you to print strings by using .", but many computer
languages allow you to manipulate them in all sorts of ways, so that you can do as
many different things with strings as you can with numbers. I shall show you a few
examples here just to set you off.
First, we want to find a way of putting strings on the stack, but it is easiest not to
do it directly. Whereas we always know how much space a number takes up — two
bytes for a single length integer, four for a double length integer or floating point
number — a string could be any length. Therefore we keep the string somewhere else
in memory, and put on the stack its address and length. This is exactly the form
required by TYPE, so we already know how to print a string.
The next step is to find a way of setting up variables that store strings instead of
numbers. We shall do this with a defining word STRING. Its defining part sets up a
parameter field that contains, first, one byte for the length of the string (which must
therefore be no more than 255 characters), and then the string itself, read in from the
input buffer. The action part of STRING will convert the parameter field address into
the address and length of the string itself — our usual form for specifying a string on
the stack. We use the word COUNT from chapter 16.
DEFINER STRING
ASCII " WORD COUNT DUP C,
OVER + SWAP
DO
IC@ C,
LOOP
DOES>
COUNT
124
STRINGS AND ARRAYS
You define a string like this, typing it all in one bufferful:
STRING FREDSADDRESS 23, Flightpath Lane, Heathrow, 01-750
Sorry, I missed that."
The double quote " at the end marks the end of the string, because we used
ASCII " WORD in STRING. This enables you to use spaces in the string.
The simplest thing you can do with your string now you've set it up is print it out:
FREDSADDRESS TYPE
A useful operation on strings is taking sections of them, or substrings. This is called
slicing. To specify a substring, you say whereabouts in the original string it starts and
finishes. In FREDSADDRESS, "Flightpath Lane" is a substring, starting at the 5 th
character (assuming you put a space after the comma) and finishing at the 19th. We
shall define a word SLICE that enables you to say
FREDSADDRESS 5 19 SLICE TYPE
to get "Flightpath Lane" printed out.
SLICE has four operands, namely the address and length of the bigger string, and
the start and finish within that of the substring. Its two results are the address and
length of the substring. Since this is still the usual format for a string, you can do
anything to a substring that you could to the original. You can print it out or even slice
it again.
: SLICE
( address, length, start, finish — address, length)
SWAP 1 MAX 3 PICK MIN 1-
( address, length, finish, start—1)
SWAP ROT MIN OVER MAX
( address, start—1, finish)
OVER — ROT ROT + SWAP
This definition of SLICE also takes care of the cases when the start is too small, or
the finish is too big, or the finish is less than the start.
The next facility we shall describe is for comparing strings. The obvious test is for
two strings to be equal, i.e. to have the same characters in the same order. A more
subtle test is for one string to come before another in an extended alphabetical order,
and we use the symbols < and > that are used with numbers to mean 'less than' and
'greater than'. We consider one string to be less than another if it comes first in
alphabetical order: thus "animal" is less than "bird", "five" is less than "four".
The rules here for determining alphabetical ordering are slightly different from
usual, and rely on ASCII codes. To compare two strings, you compare them character
125
CHAPTER 21
by character looking for a place where they differ, and then compare the different
characters. This is the usual process that tells you that "boojum" is less than "book".
Since all capital letters have smaller ASCII codes than lower case letters, they are
also smaller in the string sense. Thus, unexpectedly, "Zoo" is less than "aviary" and
"FORTH" is less than "Forth".
Here are some words to define $=, $< and $> (the string versions of =, < and >).
: 0 <>
( n - flag)
(tests for n non-zero)
0 = 0 =
: +COUNT
(used in CHECK)
4 ROLL 1+4 ROLL 1-
: CHECK
( addrl, lengthl, addr2, Iength2 - addr3, Iength3,
addr4, Iength4)
( Adjusts the addresses and lengths of two strings to
miss out any initial characters where they agree)
BEGIN
3 PICK 0<> OVER 0<> AND
5 PICK C@ 4 PICK C@ = AND
WHILE
( while neither string finished, & they still agree)
+COUNT +COUNT
REPEAT
: <DROP
(a, b- b)
SWAP DROP
■ <jj =
(al, II, a2, 12-flag)
CHECK( now strings equal if both lengths are 0)
<DROP OR <DROP ( now have dropped addresses
and ORed together lengths)
0 =
126
STRINGS AND ARRAYS
: $<
(al, II, a2, 12-flag)
CHECK ROT ( a3, a4, 14, 13)
OVER 0<> OVEROo AND
IF
( neither string has run out, so compare different characters)
DROP DROP C@ SWAP C@ >
ELSE
( one string starts off the other)
> <DROP <DROP
THEN
" $>
(al, II, a2, 12-flag)
4 ROLL 4 ROLL $<
The crucial word here is CHECK, which checks along both strings, character by
character, until it finds a place where they differ. Thus CHECK applied to "boojum"
and "book" ends up with their substrings "jum" and "k".
There are some more ideas about strings in the exercises.
Arrays
An array is a variable that can store more than one number, like MONTHS; these
numbers are called the elements of the array, so MONTHS has twelve elements,
namely 31, 28, 31 and so on. To specify a particular element, you use its position
within the array, and this number is called the subscript (in MONTHS, the subscripts
are 1 for January, 2 for February, etc.).
Because we defined MONTHS with CREATE, we had to define MONTH as well to
turn a subscript into the address of an element. If we had used DEFINER to define a
word ARRAY, we could then have used ARRAY to define MONTHS and MONTHS
could have both stored the elements and processed the subscripts. (Exercise: do
this.)
A more complicated kind of array uses two subscripts for each element: it is called
a two-dimensional array. You should imagine the elements as being arranged in a
rectangular table, with the first subscript specifying the row and the second the
column. This would be the natural way of storing something like a chess position.
(The computer of course, has to store the table row by row.)
Here are some words to enable you to define two-dimensional arrays. These are
only examples, because there are lots of facilities you could build in or leave out (see
127
CHAPTER 21
the exercises). Forget the string words first, to give yourself room.
: 2 *
( n — 2*n)
DUP +
: ROW
Row error"
CR
: COLUMN
Column error"
CR
: MESSAGE
.' Please FORGET this word."
CR ABORT
: ROW?
IF
ROW ABORT
THEN
: COLUMN?
IF
COLUMN ABORT
THEN
: DEFINER 2—D
( no. rows, no. columns —)
DUP 1— 0<
IF
( no. columns is 0 or less)
COLUMN MESSAGE
THEN
OVER DUP 1—0<
IF
( no. rows is 0 or less)
ROW MESSAGE
128
STRINGS AND ARRAYS
THEN
C, DUP C, * 2* ALLOT
DOES>
(row, column, address of array — address of element)
ROT ROT 3 PICK ( addr, row, column, addr)
C@ 3 PICK DUPI-
tX ROW? < ROW? ( error message if row wrong)
DUP 1- 0< COLUMN?
3 PICK 1+ C@ DUP
3 PICK < COLUMN? ( error message if column wrong)
ROT 1- * + 2* +
To use 2 - D you'd say, e.g.
8 8 2-D CHESSBOARD
1 12 2- D MONTHS
and then fill in the elements however you want. You use the elements just like
variables, with @ and !. For instance to set the element at row 2, column 5 of
CHESSBOARD to 1, you'd say
1 2 5 CHESSBOARD!
specifies elemenrt
specifies element and to get its value back to the stack,
25 CHESSBOARD @
Summary
Strings, substrings, string comparison
Arrays, elements, subscripts, dimensions.
Exercises
1. Here are some more facilities for strings.
(a) Assignation: given two strings on the stack (specified as usual by addresses
and lengths), copy the characters from the second one into the first. If the second
string is too long, cut it off at the end; if it is too short, pad it out at the end with
spaces.
129
CHAPTER 21
(b) A word to leave just the length of a string (by dropping its address from the
stack).
(c) A word to leave the ASCII code of the first character in a string.
2. There are many possibilities for arrays.
(a) When the address of an element is calculated, the subscripts can be checked
to make sure they are not too small or too big - as in 2- D. Alternatively, this checking
could be missed out for extra speed.
(b) We specified upper bounds for the arrays, i.e. how big the subscripts can be; it
was understood that they couldn't be less than 1. It is also possible to specify lowe
bounds to say how small the subscripts can be.
(c) It is possible to have three-dimensional arrays or even worse; the number of
dimensions for an array is the number of subscripts needed for each element. More
generally, it would be possible for the defining word - no longer 2-D, but DIM, say -
to read the number of dimensions off the stack first of all, and put this in the
parameter field; then it would start taking the bounds themselves off the stack.
(d) An array could have one byte for each entry instead of two; this would make it
useful for storing characters or small numbers. Going in the other direction, an array
could have four bytes for each entry, for floating point numbers or double length
integers.
(e) The defining word could initialize all the elements to 0 when it defines an array.
(f) An array could be a constant array, which gives an element itself rather than its
address. The elements would need to be found on the stack when the array is
defined.
Some of these are harder to deal with than others. See what you think of them.
3. Two words often found in FORTH, although not in Ace FORTH, are MOVE and
CMOVE. They are quite useful when dealing with strings. Both copy information
from one part of memory to another.
MOVE (addressl, address2, n-) copies the contents of n 2-byte cells starting at
addressl into the memory starting at address2.
CMOVE (addressl, address2, n-) is similar, but copies n bytes instead of n 2-byte
cells.
In both MOVE and CMOVE, if n is 0 or negative then nothing happens. Try writing
your own definitions for these.
A problem can arise if the block you are reading from overlaps with the block you
are writing to: in this case you must be careful which end of the block you start
copying from.
A related word (again not available on the Ace) is FILL (address, n, byte -) which
fills the memory starting at the given address with n copies of the given byte.
130
Chapter 22
VOCABULARIES
Here is a feature of FORTH that you probably won't use so much. It allows the
dictionary to be split up into various vocabularies, each with a name (the only
vocabulary that exists initially is called FORTH). When you choose to use one
vocabulary rather than another (by typing in its name), any dictionary search is
confined to that vocabulary. This means that you can have two words with the same
name, but in different vocabularies so that you can get different interpretations of the
word depending on which vocabulary is active.
Suppose you wanted to redefine numbers so that when you type them in, they
print out a bingo caller's tag: e.g.
: 88
. " Two fat ladies"
This is quite permissible; it means that when you use 88 it is found as a word in the
dictionary and prints "Two fat ladies". (Normally for a number, no definition is found
in the dictionary and the next step is to try to work it out as a number instead of as a
defined word.) However, it is rather dangerous to have these redefined numbers
lying around, so FORGET 88.
Much safer is to have a separate vocabulary, BINGO, to contain them. At the
moment, the only vocabulary in the dictionary is FORTH, the standard one. To set up
BINGO, type
VOCABULARY BINGO
This does two things:
1. It defines a vocabulary word BINGO, whose function is to activate the BINGO
vocabulary. A rather odd feature is that this word BINGO is actually in the FORTH
vocabulary.
2. It sets up a new vocabulary called BINGO. So far the vocabulary BINGO
contains no words of its own (not even the word BINGO), but it does contain all the
words of the vocabulary FORTH.
FORTH is BINGO's parent, because BINGO's vocabulary word was set up in the
FORTH vocabulary. The general rule when searching a vocabulary for some word is
that if it is not found the search carries on through the parent vocabulary.
To see this, use VLIST. You will see a list of words in the FORTH vocabulary,
starting with the vocabulary word BINGO.
131
CHAPTER 22
Now type BINGO. This makes BINGO the context vocabulary, i.e. the one in which
to look for the words you type in. This is also the vocabulary listed by VLIST, so
VLIST now will list the BINGO vocabulary: first the words properly in the BINGO
vocabulary (there aren't any yet), and then the words in BINGO's parent, FORTH. You
won't yet see any difference between this VLIST, for BINGO, and the previous
one, for FORTH.
Now let's define some words for BINGO. BINGO is already the context vocabulary
(because you typed BINGO), but this only affects searches for words. New
definitions are entered into the current vocabulary, which is still FORTH. To make
BINGO the current vocabulary, use the word DEFINITIONS: this makes the current
vocabulary the same as the context vocabulary. You must use DEFINITIONS to allow
yourself to enter new word definitions into the context vocabulary.
By now BINGO should be both the context and current vocabularies, so start
defining the bingo tags.
: 88
. " Two fat ladies"
: 21
. " Key of the door"
: 189
My age"
Now if you do VLIST, you can see the new words 88, 21 and 189 in the BINGO
vocabulary. If you do FORTH and VLIST, you will see only the words in the FORTH
vocabulary and the numbers 88, 21 and 189 will have regained their proper meanings.
Here more precisely is how vocabularies work. We said in chapter 20 that when a
word is defined its header contains a link that points to the previously defined word in
the dictionary. This is not in fact quite true; the link points to the previously defined
word in the current vocabulary, and there may have been words defined in another
vocabulary between the two. For instance in our example 189 is linked to 21 which is
linked to 88: this linked chain makes up the BINGO vocabulary. 88 is linked to a kind
of trick that joins the BINGO vocabulary to its parent vocabulary, FORTH.
If you did FORTH DEFINITIONS to make FORTH the current vocabulary, and then
defined another word, it would be linked to the word BINGO (which, you will
remember, is actually in the FORTH vocabulary). Thus the vocabularies can be
interleaved in the dictionary.
Let us summarize what we've said so far:
1. Initially there is only one vocabulary, FORTH.
2. Two of the vocabularies (possibly the same one) are in use at any given time:
the context vocabulary is used when looking for words, and the current vocabulary is
132
VOCABULARIES
used when defining new words.
3. VOCABULARY defines a new vocabulary and its associated vocabulary word
(but the vocabulary word is contained not in the new vocabulary, but in its parent, the
current vocabulary).
4. A vocabulary word is used to specify the context vocabulary; DEFINITIONS
makes the context vocabulary the current vocabulary as well.
5. When the system is searching for a word you have just typed in, it starts off in
the context vocabulary, and, if necessary, continues with its parent. It is bound to end
up in FORTH, because FORTH is Adam and Eve as far as vocabularies are concerned.
The parameter field of a vocabulary word is laid out as follows:
First, two bytes containing the address of the name length field of the newest
word in the vocabulary. This shows both where to start any search through the
vocabulary and where to link the next word to be defined in it.
Next, one byte that always contains 0. This is a kind of fake name length field (no
real word has length 0) and works the trick for chaining vocabularies together. If our
vocabulary has any children, then their first (oldest) words are linked to this byte.
Finally, two bytes that contain the address (called the vocabulary linkage ) of the
corresponding two bytes in another vocabulary: not the parent vocabulary, but simply
the last vocabulary to be defined before this one. This means that all vocabularies can
be found by starting at the newest and following through from one to the previous
one. You won't usually need to use this. In FORTH, these two bytes are 0.
There are three variables associated with vocabularies. The first two are called
CONTEXT and CURRENT, and their values are the addresses of the parameter fields
of the vocabulary words for the context and current vocabularies.
The third one hasn't got a name, but its address is 15413. The value of this variable
is the address of the two bytes containing the vocabulary linkage in the newest
vocabulary. This shows where to start if for some reason you want to check through
all the vocabularies.
If you start using vocabularies, you need to be careful with FORGET and LOAD.
With FORGET, the safest rule is: don't forget words from more than one
vocabulary at once. If you forget a vocabulary word then the system of vocabulary
linkages will no longer be right. (This only matters if you want to use the vocabulary
linkages yourself.)
With LOAD, again the vocabulary linkages will no longer be right.
Summary
Vocabularies — context and current.
Vocabulary words, vocabulary linkages.
FORTH words: FORTH, VOCABULARY, DEFINITIONS, CONTEXT, CURRENT.
133
Chapter 23
INSIDE COLON DEFINITIONS
This chapter explains how to give yourself more control over colon definitions — it also
applies to definitions made by DEFINER (or, as we shall see, by COMPILER).
You must have realised by now that a word from the input buffer is treated
differently when you are in the middle of a colon definition: it's not executed
immediately, but stored away as part of the definition. We say that normally we are in
interpret mode (because the words are being interpreted and executed), but that
while a word is being defined by a colon definition we are in compile mode (the
words from the input buffer are compiled into the dictionary as part of the new
definition).
Even in a colon definition you can switch back temporarily to interpret mode, using
two words [ and ].
[ takes you into interpret mode.
] takes you into compile mode.
(Note that the computer only prints OK in interpret mode. This helps you
remember which you're in.)
For instance, suppose you have two numbers in a word definition, and you find it
natural to type them in using different number bases. You would use [ and ] to go into
interpret mode while you change BASE. The word TAB in exercise 4 of chapter 12 is
a bit like that, because the 31 is more naturally binary 11111. You might well prefer to
type it in as
: TAB
(tab stop —)
15388 @ -
[ 2 BASE C! ]
11111
[ DECIMAL ]
AND SPACES
(When you've done this, try
LIST TAB
None of the interpret mode stuff will be shown in the listing, and in fact binary 11111
will be converted to decimal 31.)
It should be clear from this that [ is in some way special, because even though the
134
INSIDE COLON DEFINITIONS
computer is in compile mode when it meets [, it executes it rather than compiling it
into the dictionary. This is because [ is what is called an immediate word, a word that
is always executed immediately even in compile mode.
You can define your own immediate words very easily. You just define them in the
usual way, and then execute the word IMMEDIATE. IMMEDIATE makes the newest
word (actually, the newest word in the current vocabulary) into an immediate word.
If you wanted to write quite a few definitions with both binary and decimal numbers,
you could define
: BASE2
2 BASE C!
y
IMMEDIATE
: BASE10
DECIMAL
y
IMMEDIATE
and then
: TAB
(tab stop -)
15388 @ -
BASE2 11111 BASE10
AND SPACES
Again, BASE2 and BASE10 don't come out in the listing of TAB.
One use of [ and ] is that you can do calculations while in the middle of defining a
word. What is more, by using an immediate word LITERAL, the results of these
calculations can be compiled into the definition as though you'd typed them straight
in as numbers in compile mode.
If you wanted to fill the screen with dots, you could use CR and then print 23 * 32
dots. Now 23 * 32 = 736, but maybe you can't be bothered to work this out. Using
LITERAL, you could define a word DOTS thus:
: DOTS
CR
[ 23 32 * ] LITERAL ( 23 32 *)
0
DO
ii ii
LOOP
135
CHAPTER 23
LITERAL (n —) takes a single length integer off the stack, and compiles it into the
dictionary in the same way as a number would normally be compiled.
Notice our comment ( 23 32 *). When you list DOTS there is no trace of the fact
that you used LITERAL, so the comment reminds you of what's going on.
We have now seen both ordinary words, which in compile mode are compiled to
execute later, and immediate words which are executed immediately. There are
quite a few very important words that must do both.
;, for instance, has an immediate effect which is to go back into interpret mode.
But it also needs to be compiled into the definition to mark the end when the
compiled words are finally executed. We say it has a compile-time (or immediate)
action ("Don't try to compile any more of this definition") and a run-time action
("Don't try to execute any more of this definition").
Similarly, LITERAL has both a compile-time action (take a number off the stack and
compile it into the definition) and a run-time action (take the number that has been
compiled just here and put it on the stack).
IF, THEN, BEGIN, DO and the rest have a similar double nature: at compile-time
they work out how the word definition is sectioned up by these structures, and at
run-time they must make any necessary jumps round the sections. Such words are
called compiling words because they compile things into the dictionary.
There is a word COMPILER that enables you to define your own compiling words,
but before I say how it works I shall explain what compiling involves in a bit more
detail.
As we know from chapter 20, every FORTH word has what is called a code field in
its header: so the address of the code field is called the code field address of the
word. (This is the address that FIND leaves on the stack.) When the word is compiled
into the definition of another word, all that happens is that its code field address is
enclosed in the dictionary (using ,). For this reason, the code field address is often
called the compilation address.
One word that uses compilation addresses is EXECUTE (compilation address —). It
takes a compilation address off the stack and executes the corresponding word, e.g.
FIND DUP EXECUTE
when typed in in execute mode does the same as DUP.
A simple colon definition has just a list of compilation addresses in its parameter
field. To execute the word, the Ace takes the first of these compilation addresses,
finds out the word whose compilation address it is, executes that word, comes back
to go on to the next compilation address, and so on. Eventually it reaches the
compilation address for [the run-time action of] ;, and then it knows it has finished
this word.
For instance, if you define
: 2 *
DUP +
136
INSIDE COLON DEFINITIONS
then the definition for 2* has
(DUP, + and ; are all in ROM, and their parameter fields are rather different.)
This is the simplest form, but quite often there must be some extra information
after the compilation address. For instance LITERAL compiles a number into the
definition, and this number is stored in the dictionary immediately after the
compilation address of LITERAL. Numbers typed directly into the definition are
compiled in the same way.
This extra information is called the operand field, and the whole caboodle
(compilation address + operand field if there is one) is called a compiled word. The
compiled word set up by LITERAL consists of its compilation address followed by the
operand field, two bytes for the number from the stack.
Another example is IF. Its operand field has two bytes saying how far to jump to
get over the IF . . . ELSE section if the condition turns out to be false.
To set up your own compiling word, you need to specify
1. how to set up the operand field: this is the compile-time action, and
2. how to execute the compiled word: this is the run-time action.
You specify both of these with two words COMPILER and RUNS> - like DEFINER
and DOES>, they always occur together.
Here is a word 2LITERAL that works like LITERAL but with four bytes instead of
two - so you can use it for floating point numbers or double length integers.
4 COMPILER 2LITERAL
SWAP , ,
RUNS>
DUP @ SWAP 2+ @
137
CHAPTER 23
The compile-time action is to take two entries off the stack and compile them into
the dictionary (with ,). This is the part before RUNS>.
The run-time action is the part after RUNS>. RUNS> leaves the address of the
operand field on the stack (rather as DOES> leaves the address of a parameter field)
so that you can fetch the four-byte number stored there.
Notice the 4 before COMPILER. This shows how many bytes you intend to put in
the operand field. It's up to you to make sure you get it right, because if you don't the
computer can get very confused. If the number of bytes might vary, then you use -1
COMPILER and your compile-time action must set the first two bytes in the operand
field to show the length of the rest of the operand field (excluding these two bytes).
( and ." use this mechanism.
To show how to use 2LITERAL, suppose you need a word to add 1/7 to a floating
point number on the stack. One way is
: 1/7+
1. 7. FI F+
However, since 1 /7 is 0.142857 the word 1/7+ would run much more quickly if you
typed it in as
: 1/7+
0.142857 F+
Now, rather than work out this number beforehand you can type it in as
: 1/7+
[1.7. FI] (1/7)
2LITERAL F+
At compile time (when 1/7+ is defined) 2LITERAL will put a compiled word into
the definition of 1/7+: first its own compilation address (or rather a compilation
address for its run-time action) and then an operand field containing the floating point
number. At run-time (when 1/7+ is executed) it will copy the number from the
operand field to the stack.
Be very careful if you edit 1/7+. The listing mechanism doesn't know what the
operand field means, so it just ignores it. LIST 1/7+ produces
: 1/7+
(1/7)
2LITERAL F+
138
INSIDE COLON DEFINITIONS
and the only record of the floating point number is in the comment. If you edit 1/7+,
you must type the calculation that leads to 1/7 in all over again.
A word that can be useful is HERE (— address). This puts on the stack the address
where the next byte enclosed in the dictionary will go. It shows how far in memory
the dictionary has reached.
Summary
Compile mode and interpret mode.
Immediate words, compiling words.
Compiled words: compilation address and operand field.
FORTH words: [, ], IMMEDIATE, EXECUTE, COMPILER, RUNS>, HERE.
Exercises
1. One way of using EXECUTE is with a compilation address stored in a variable. For
instance, you may have two different ways of dealing with unwelcome enquiries:
: RUDE
CR Go and boil your head."
J
: POLITE
CR That's very interesting, and my
CR colleague Richard would be
CR pleased to discuss it with you.
FIND POLITE VARIABLE MOOD
As it stands, MOOD contains the compilation address of POLITE. But if you wake
up with a hangover, or if Richard starts sending the enquirers back to you, you could
say
FIND RUDE MOOD !
In either case, to get the appropriate response you use
MOOD @ EXECUTE
This is quite useful, but there is a big drawback if you ever use REDEFINE:
REDEFINE can change the compilation addresses of words, but it wouldn't have any
way of knowing that it ought to adjust the values of variables like MOOD and you'd
have to do this yourself. The compilation addresses that are likely to change are those
of the words that are newer than the old version of the word you redefine.
Similarly, LOAD can change compilation addresses. If there is already a dictionary
in memory when you load a new one in from tape, the new dictionary will have all its
compilation addresses changed.
139
Chapter 24
HOW THE MEMORY IS LAID OUT
Various parts of the memory have various uses in the Ace; some have ROM, some
have RAM and some aren't used at all. Here we describe the various parts giving
their addresses in both hex (first) and decimal (afterwards, in brackets).
0 (0) to 1FFF (8191): this uses ROM and so the information stored there is indelibly
built into the computer (unless you take it through one of those X-ray machines at an
airport). It contains computer programs to tell the Ace what to do, including all the
Ace's own FORTH word definitions.
2000 (8192) to 23FF (9215): this is exactly the same RAM as is at addresses 2400 to
27FF, so each of these RAM bytes has two addresses. Reading or writing has a
different effect depending on which address you use. The smaller address gives
priority to the FORTH program and the larger address gives priority to the circuitry
that produces the television picture. This is explained more fully in Chapter 14,
Exercise 1.
2400 (9216) to 26FF (9983): this is the video RAM. It stores the television picture,
with the ASCII code of each of the 24*32 characters that make it up.
2700 (9984): this is RAM and should always contain the byte 0.
2701 (9985) to 27FF (10239): RAM, storing the pad. (See Chapter 16.)
2800 (10240) to 2BFF (11263): more addresses for the RAM between 2C00 and
2FFF, with the same distinction as for the video RAM.
2000 (11264) to 2FFF (12287): this is the character set RAM, containing the dot
patterns for 128 characters. See Chapter 12. Note that you can't read back from this
RAM: you can only write to it.
3000 (12288) to 3BFF (15359): three identical copies of the RAM 3000 to 3FFF.
3C00 (15360) to 3C3F (15423): this RAM contains the system variables. These are
various pieces of information that the Ace needs to store, and they're described more
fully below.
3C40 (15424) to 3FFF (16383(: RAM containing the dictionary, the stack and the
return stack.
The dictionary starts at 3C40 with the vocabulary word FORTH, and continues with
your own words. Where it finishes depends entirely on how many words there are.
After the dictionary there are twelve bytes unused (so that stack underflow doesn't
corrupt the dictionary) and then the stack, growing upwards through memory.
The return stack starts at 3FFF and grows down in memory, towards the stack.
When they get too close there is no more room left and you get ERROR 1.
Dictionary
12 Bytes
Stack —►
Return stack
3C40
3CFF
140
HOW THE MEMORY IS LAID OUT
It is possible to provide more memory for the dictionary and stacks (it is plugged in
at the back of the computer), and this will start at address 4000 (16384) and may
extend up as far as FFFF (65535). However much you provide, the return stack will
start at its top end (the end with the highest address) and work down.
System variables
Here is a list of system variables. We have given them all names, but that is just for
ease of reference. The Ace will not recognize these names, except for a few, like
BASE, that are FORTH words. I've written these FORTH words in bold type in the
usual way.
FP_WS 3C00 (15360) 19 bytes used as work space for floating point arithmetic.
LISTS 3C13 (15379) 5 bytes used as work space by LIST and EDIT.
RAM 3C18 (15384) 2 bytes — the first address past the last address in RAM. If
you want to set aside some RAM at the top end as not being available for the
dictionary and stacks, then store its starting address at RAM and do QUIT. QUIT
clears the return stack and starts it off again at the address stored in RAM.
HOLD 3C1A (15386) 2 bytes. The address of the latest character held in the pad by
formatted output (#, HOLD and so on).
SCRAPS 3C1C (15388) 2 bytes. The address of the place in the video RAM where the
next character is to be printed (i.e. the print position). The example TAB (Chapter 12,
Exercise 4) uses this.
INSCRN 3C1E (15390) 2 bytes. The address of the start of the current logical line
(what I called before a computer line) in the input buffer.
CURSOR 3C20 (15392) 2 bytes. The address of the cursor in the input buffer.
ENDBUF 3C22 (15394) 2 bytes. The address of the end of the current logical line in
the input buffer.
L_HALF 3C24 (15396) 2 bytes. The address of the start of the input buffer. The input
buffer itself is stored in the video RAM, where you see it.
KEYCOD 3C26 (15398) 1 byte. The ASCII code of the last key pressed.
KEYCNT 3C27 (15399) 1 byte. Used by the routine that reads the keyboard.
STATIN 3C28 (15400) 1 byte. Used by the routine that reads the keyboard.
EXWRCH 3C29 (15401) 2 bytes. This is normally 0, but it can be changed to allow
141
CHAPTER 24
printing to be sent to some device (e.g. a printer) other than the television screen.
EXWRCH must be given the address of a machine code (see the next chapter)
routine to output a character. The character is provided in the A register of the Z80.
The output routine should preserve the auxiliary registers, ix and iy, and finish off with
exx and ret.
FRAMES 3C2B (15403) 4 bytes. These four bytes form a double length integer that
counts the time since the Ace was switched on, in 50ths of a second. It can thus be
used as a clock. Here are some words to use it.
: SETCLOCK
( hours, minutes —)
BEGIN
INKEY 0= ( wait for ENTER to be released)
UNTIL
CR . " Press a key to set clock"
SWAP 60 * + (time in minutes)
3000 U* (time in 50ths of a second, double length)
BEGIN
( wait for key depression)
INKEY
UNTIL
015403!
15405!15403
(Exercise 3 explains the rather devious way in which we set the counter.)
: MINSEC
( double length seconds or minutes — single length minutes or hours)
( writes seconds or minutes to pad)
60 U/MOD SWAP
0 # # DROP DROP (write seconds or minutes)
ASCII : HOLD
: TIME
( prints time using formatted output)
15403 @ 15405 @
OVER OVER D+ (time in lOOths of a second)
<# # # ( print fractions of a second)
ASCII . HOLD ( now double length time in seconds on stack)
MINSEC ( print seconds, leave single length time in minutes)
0 MINSEC ( print minutes, leave single length time in hours)
0 #S #> TYPE
142
HOW THE MEMORY IS LAID OUT
Warning — if these four bytes ever reach hex FFFFFFFF then the next change, to
00000000, will also affect the system variable XCOORD. Also, note that BEEP and
tape operations temporarily stop the frame counter.
XCOORD 3C2F (15407) 1 byte. The x-coordinate last used by PLOT. DRAW, in
Chapter 13, exercise 1 uses this to tell it where to start the line.
YCOORD 3C30 (15408) 1 byte. The y-coordinate last used by PLOT.
CURRENT 3C31 (15409) 2 bytes. The parameter field address for the vocabulary
word of the current vocabulary. See Chapter 22.
CONTEXT 3C33 (15411) 2 bytes. The parameter field address for the vocabulary
word of the context vocabulary. See Chapter 22.
VOCLNK 3C35 (15413) 2 bytes. The address of the fourth byte in the parameter field
— the vocabulary linkage — of the vocabulary word of the most recently defined
vocabulary. See Chapter 22.
STKBOT 3C37 (15415) 2 bytes. The address of the next byte into which anything will
be enclosed in the dictionary, i.e. one byte past the present end of the dictionary.
HERE is equivalent to 15415 @.
DICT 3C39 (15417) 2 bytes. The address of the length field in the newest word in the
dictionary. If that length field is correctly filled in, then DICT may be 0.
SPARE 3C3B (15419) 2 bytes. The address of the first byte past the too of the stack.
Note — because of the way @ works, 15419 @ will give the address of the top entry
on the stack.
Here is a word that prints out the entire stack, starting at the bottom, without
destroying it.
: .S
15419 @ HERE 12 +
(top, bottom)
OVER OVER —
IF (if stack not empty)
DO
I @ ■ 2
+LOOP
ELSE
DROP DROP
THEN
143
CHAPTER 24
ERR_NO 3C3D (15421) 1 byte. This is usually 255, meaning "no error". If ABORT is
used, and ERR_NO is between 0 and 127, then "ERROR" will be printed out,
followed by the error number ERR_NO.
FLAGS 3C3E (15422) 1 byte. Shows the state of various parts of the system, each bit
showing whether something particular is happening or not. Some of these may be
useful.
Bit 2 (the 4s bit in binary), when 1, shows that there is an incomplete definition at
the end of the dictionary. If ABORT is executed, this definition (its address is inferred
from DICT) is taken out of the dictionary.
Bit 3 (the 8s bit), when 1, shows that output is to be fed into the input buffer.
Bit 4 (the 16s bit), when 1, shows that the Ace is in invisible mode.
Bit 6 (the 64s bit), when 1, shows that the Ace is in compile mode.
BASE 3C3F (15423) 1 byte. The system number base.
Exercises
1. Try this-
:SYSVARS
( shows system variables continuously)
CLS
BEGIN
0 0 AT 15360 80 TYPE
0
UNTIL
1
You should easily be able to see FRAMES counting away; the other flashing
variable is KEYCNT. Just to the left of KEYCNT is KEYCOD - you can see this work if
you press a key. Towards the end is the header for FORTH.
Why does it all flash every five seconds? (Hint: consider what happens when the
least significant byte of FRAMES reaches 13, the ASCII code for carriage return.)
2. The frames counter FRAMES is not updated exactly every 50th of a second, but
every 624/625 50ths of a second. This means that the word TIME I have given will
gain one second in 625, or approximately 214 minutes a day. If you want very accurate
timing, you'll need to correct this.
3. Suppose you are reading FRAMES, by
15403 @) 15405 @
and the less significant two bytes turn out to be 65535 (i.e. their maximum). It is
possible that in between reading the two parts FRAMES will be updated: the two
144
HOW THE MEMORY IS LAID OUT
bytes at 15403 will go down to 0, and the two at 15405 will be increased by 1. The
double length integer you have read is therefore 65535 too big (about 20 minutes).
This sort of thing can happen even within so its quite pernicious. However, there
is a way round: if you want to know the exact time at a given moment, then read
FRAMES twice (each time read 15403 first and then 15405) and take the smaller of
the two answers.
The same sort of problem arises in SETCLOCK. We first set the two bytes at
15403 to 0, so that we know these can't interfere with the values we are writing in by
inconveniently going from 65535 to 0.
4. How long does FRAMES have to count for before it reaches hex FFFFFFFF?
5. In Chapter 20 I said that if you have used DEFINER to make a new defining word
of your own, and if your word runs into an ERROR while it is half-way through
defining another word, then this half-defined word won't be erased from the
dictionary. This is why in Chapter 21 we needed the word MESSAGE in case this
happened while you were defining a new array.
Bit 2 in FLAGS lets you force the partial definition to be erased when ABORT is
executed: you'd use
: MESSAGE
( Sets bit 2 of FLAGS to 1 and aborts)
15422 C@ 4 OR 15422 C! ABORT
145
Chapter 25
MACHINE CODE
You will think of the Ace just as a computer that understands FORTH, but that isn't
the whole story. At the heart of the Ace is a relatively simple-minded but very
hard-working component called a processor chip (or, often, CPU which stands for
Central Processor Unit). It has to read the keyboard, interpret your typing, execute
your FORTH programs and display the results, even though by itself it knows nothing
at all about FORTH. It has to be told everything by instructions stored in ROM.
The instructions (of course) can't be written in FORTH, and in fact they are written
in machine code which is the only form the processor understands. In machine code,
each instruction is coded into bytes — usually one, sometimes more. When the
processor is first switched on, it wakes up and thinks, 'Where was I? I've forgotten.
I'd better start at the beginning.' So it fetches a byte from the beginning of memory
(address 0) and obeys it as a machine code instruction. After that it goes on to the
next instruction and obeys them one after another in sequence; or, if the instructions
tell it to, it'll start obeying them from somewhere else in memory. Some of these
instructions tell it how to execute FORTH programs.
There are many different kinds of processor, each with its own variety of machine
code. The one in the Ace is a Z80 (actually a Z80A, which works faster), a processor
originally designed by Zilog Corporation and now used on many different computers.
Explaining Z80 machine code could easily fill another book the size of this one, so
I'm not going to try. If you don't already know about it but you want to learn, find
yourself a small book whose title and blurb say something to the effect of 'Z80
machine code (or assembly language) for the beginner.' In the meantime, you can read
the rest of this chapter but you mustn't expect to understand all of it.
Programming in machine code gives you three main advantages over normal
languages: the programs are faster and take up less space, and you also have more
control over the innermost corners of the computer. FORTH is already pretty good in
all three respects, so there is less need to use machine code on the Ace than on a
computer that doesn't use FORTH. However, it can still be useful.
To use machine code, you use a FORTH word CALL (address -). CALL takes off
the stack the address of the start of some machine code, and it starts obeying the
machine code instructions there. This carries on until an instruction jp (iy) is found.
(Each instruction has not only the form coded into bytes, but also a readable name
called its mnemonic, jp (iy) is a mnemonic form.) The code for jp (iy) is two bytes, 253
followed by 233 (you can look up the codes in Appendix A).
The easiest place to put the machine code itself is in the parameter field of a
FORTH word, using a defining word
146
MACHINE CODE
DEFINER CODE
DOES>
CALL
The action of a word defined by CODE is to run the machine code in its parameter
field; you set up the parameter field using C,.
Here is an example. The machine code instruction halt (code 118) makes the
processor stop until it receives a signal called an interrupt. On the Ace, the rest of the
computer gives the processor an interrupt every 50th of a second, so you can use
halt for timed pauses.
The machine code you need is
halt 118
jp (iy) 253, 233
so let us put these three bytes into the parameter field of a word HALT.
CODE HALT 118 C, 253 C, 233 C,
When you execute HALT, the action part of CODE will CALL this machine code.
You can now define PAUSE.
: PAUSE
(length of pause in 50ths of a second —)
0
DO
HALT
LOOP
If you already know about machine code on the Z80 you'll find it useful to know
what some of the restart instructions do.
rst 8 (code 207) outputs a character from the A register. This normally goes to the
television screen, but you can make it use a different routine for another device by
setting the system variable EXWRCH suitably. (See chapter 24.) This restart uses the
A register and the auxiliary B, C, D, E, H and L registers.
rst 16 (code 215) puts the DE register pair onto the FORTH stack. It uses the H and L
registers.
Note that the Ace is different from many Z80 based FORTH systems in that it uses
the machine stack as the return stack, and sets up the data stack by more laborious
means.
147
CHAPTER 25
rst 24 (code 223) takes off the top of the FORTH stack and puts it in the DE register
pair. It uses the H and L registers.
rst 32 (code 231) is essentially ABORT. The restart instruction should be followed
by a byte containing an error code; this is put in the system variable ERR NO before
ABORT is executed.
Here are a few warnings for those that understand.
1. The IX and IY registers can be used, but they must have their original values
restored at the end (but see below regarding IY). All other registers may be freely
used, but don't do anything silly with the stack pointer SP.
2. Remember that REDEFINE and LOAD can move words about in the dictionary
(see chapter 23, exercise 1). Therefore, if you keep machine code in the dictionary as
recommended above with HALT, then either it must be relocatable (movable) or you
must be very careful with REDEFINE and LOAD.
The fact that a machine code routine used by CALL ends with jp (iy) applies to all
FORTH words defined as machine code. Such words are called primitives, and there
are many amongst the 142 FORTH words in ROM. This means that you can affect
what happens at the end of each primitive by adjusting the IY register.
Normally, the IY register points to some machine code that checks for errors
before carrying on: it checks for space (that the data stack isn't getting too close to
the return stack), for stack underf low (on the data stack), and it checks the BREAK
key. However, you can save time by making the IY register point straight to the code
that goes on to the next word. You don't actually need to know anything about IY
registers to do this, because of two words FAST and SLOW.
FAST (—) turns off all the error checking, so that programs run approximately 25%
faster.
SLOW (—) turns the error checking back on again.
Remember, FAST is dangerous. You should only use it with words that you have
tested thoroughly and know you can trust. Remember especially that the BREAK key
won't be tested. SLOW isn't much slower, and it's safe.
Summary
Z80 machine code.
FORTH words: CALL, FAST, SLOW.
Exercises
1. Elaborate on CODE so that it gives you more help in setting up the machine code
parameter field. For instance, you could leave the machine code bytes on the stack,
and tell it how many there are. Maybe it could put in the jp (iy) automatically. One
common system on FORTH computers is to have a whole ASSEMBLER vocabulary
that helps you by letting you use mnemonics. For instance, the ASSEMBLER
vocabulary would have its own word HALT defined as
148
MACHINE CODE
: HALT
118 C,
(very different from our HALT). Then one of the things that CODE would do is make
ASSEMBLER the context vocabulary.
2. Modify PAUSE so that it comes out of its pause if you press a key.
3. If you were wondering how the code field of a word worked, I can now tell you. It
is the address of some machine code instructions that are obeyed whenever the
word is executed. They may make up the entire word — as for a primitive — or they
may just explain how to execute a sequence of FORTH compiled words — as in a
colon definition.
In a primitive, the code field is just the parameter field address and the parameter
field contains the machine code. DUP is a primitive, so
FIND DUP 2+ CALL
and
FIND DUP @ CALL
both execute DUP. There are easier ways, of course.
149
Chapter 26
EXTENDING THE ACE
Extra electronics (peripherals) can be plugged in at the back of the Ace by clipping
them directly onto the circuit board through a hole in the case. Normally you'd buy
such extensions ready-made by someone else and possibly with a tape of programs
enabling you to use it. Typical peripherals are extra memory, or connections to
printers, and if they are any good they'll be supplied with full instructions.
If you're more adventurous you may feel like making up your own peripherals and
you will need to consider how to let your electronics communicate with your FORTH
programs, and how physically to connect it to the computer.
First, the communication. The Z80 processor communicates with the rest of the
world by using electrical voltages on wires, and the voltages are very crudely
measured as being either high or low. These two levels can therefore represent bits
(0 or 1). Eight wires grouped together can represent a byte, and there is such a group
called the data bus; sixteen wires grouped together can represent a single-length
integer, and there is such a group called the address bus.
The processor uses these to read from or write to memory. When it wants to write
a byte to memory, it puts the address on the address bus and the byte on the data
bus, and puts out a signal meaning, 'Ahoy there memory! If this is your address then
remember the byte on the data bus.' When it wants to read a byte, it puts the
address on the address bus and then a signal saying 'If this is your address then
please put your remembered byte on the data bus for me.' Then it reads the data bus.
There is a very similar system for what are called I/O (input/output) devices such as
your peripheral. Again there are 65536 I/O slots (called ports), each with its own port
address, and the processor communicates with them a byte at a time. When it wants
to write a byte to or read a byte from one of them it uses exactly the same system as
it did with the memory except that it says 'Ahoy there I/O devices!' and the memory
ignores it. It is up to your peripheral to watch out for these signals.
To talk to the I/O ports from a FORTH program, you use two words IN and OUT:
these are the I/O equivalents of the memory words C@ and C!.
IN (address — byte) reads a byte from the port with the given address.
OUT (byte, address —) writes the byte to the port with the given address.
When choosing a port address for your peripheral, avoid ones that are already used
by something else. The even addresses are used by the Ace itself, so only use odd
addresses.
Here are two simple applications joined together in a single package of electronics.
One controls traffic lights, and the other turns a relay on and off. They occupy a single
150
EXTENDING THE ACE
I/O port (with address 1) and expect the data bytes output by the computer to be
coded as follows:
1 turn on red light
2 turn on amber light
3 turn on red and amber lights
4 turn on green light 8 turn on relay
0 turn everything off
j
and turn everything
else off
Here are the FORTH words to use:
: CHANGE
(data —)
( writes to I/O port 1)
1 OUT
: ROFF
(-)
(turns relay off)
0 CHANGE
: RON
(-)
(turns relay on)
8 CHANGE
: WAIT
( delay — delay) DUP 0
DO
LOOP
DEFINER LIGHT
(light code —)
C,
DOES>
( address of light code —)
C@ CHANGE WAIT
151
CHAPTER 26
1 LIGHT RED
2 LIGHT AMBER
3 LIGHT RED&AMB
4 LIGHT GREEN
:SEQUENCE
(delay — delay)
RED RED&AMB GREEN AMBER
: RUNLIGHTS
(delay —delay) BEGIN
SEQUENCE 0
UNTIL
e.g. 10000 RUNLIGHTS will sequence through the lights repeatedly, using 10000 to
determine how long WAIT waits at each colour.
This covers the software design; here is a circuit diagram of the electronics
needed.
Traffic Light Controller Use 2N3904 transistors
The signals to this circuitry (A1, IORQ and so on) need to be connected to the
computer through an edge connector, a connector that clips over the back edge of
the circuit board. Here is the arrangement of the signals on the computer.
SLOT
Top
EARTH
1 1
+9V
-1—
+5V
$
—1(
All
J
A12
— I —
A13
—1—
A15 D3
AM D4
1 1 1 |
05
1
A2
1
A10
1
A9
A8
A 1
1
aj?
|
Dt
I
D6
1
WE
1
Under side
1 1
NMI
1 1
MREQ
1
1
RD
1
1 1 1 1
BUSAK BUSRQ
1
Ml
1
1
A6
A5
A4
"T"
A3
A1
DJ2T
■~r
02
H —
D7
INT
HALT
IORO
WR
WAIT RESET
RFSH
SLOT
152
EXTENDING THE ACE
Here is another example, this time for input. It reads six switches, each of which
can be either open (0) or closed (1), and the byte read in is made of the six
corresponding bits with two bits undetermined.
DEFINER SWITCH
( bit value —)
C,
DOES>
( address of bit value — flag)
(leaves 1 if the switch is closed, 0 if open)
C@ 1 IN AND 0= 0=
1 SWITCH SO
2 SWITCH SI
4 SWITCH S2
8 SWITCH S3
16 SWITCH S4
32 SWITCH S5
The words SO, S5 test the corresponding switches and leave a flag on the
stack, 1 if the switch is closed.
We could use switch 0 as a Morse key:
: MORSE
BEGIN
SO
IF
100 10 BEEP
THEN
0
UNTIL
Here is a word CHECK to check for all the switches being closed, and a word
ALARM to raise an alarm unless this happens.
: CHECK
( — flag)
1 IN 63 AND 63 =
153
CHAPTER 26
: ALARM
BEGIN
CHECK 0=
IF
100 1000 BEEP
THEN
0
UNTIL
Again we've used I/O port address 1 here. This is the circuit diagram:
Switch detector
+5V 0V
+5V
IK
RD
IORQ
A1
1
OV
Enable
00 -— 3
D1 5
D2
D3
DA
D5
1
15
16
8
7
9
11
13
2
4
6
10
12
14
+5V
S0
0V
1 K
SI
V
0V
/4LS368
Switches S2 to S5 are similar to SO and SI, each with a IK resistor, and are
connected to pins 6, 10, 12 and 14.
Our peripherals here used an I/O port, number 1. It is also possible for peripherals
to use memory addresses and they are then called memory-mapped peripherals.
They must be careful not to use memory addresses used by genuine memory (see
chapter 24).
All even I/O port addresses are reserved for the Ace's internal use, although it only
actually uses eight of them, namely (in hex) FEFE, FDFE, FBEE, F7FE, EFFE, DFFE,
BFFE and 7FFE. You can see that the less significant byte is always FE, while if you
convert the more significant byte to binary there is one 0 bit and the rest are Is.
When one of these ports is read from,
(a) Half a row of the keyboard is read - which half row depends on which bit is 0 in
the more significant byte of the port address. The least significant five bits of the data
show whether a key is pressed (0) or not (1).
(b) The signal at the EAR socket is read to bit 5 of the data.
(c) The diaphragm of the loudspeaker is pushed in.
When one of these ports is written to,
(a) Bit 3 of the data is written to the MIC socket.
(b) The diaphragm of the loudspeaker is pushed out.
Summary
I/O ports, port addresses.
FORTH words: IN, OUT.
154
Top
EXTENDING THE ACE
Exercises
1. Rewrite the word CHANGE so that instead of driving external circuitry it shows
something on the television screen.
2. Try this:
NOISE
[ 16 BASE C! ]
BEGIN
FEFE IN FEFE OUT 0
UNTIL
Awful, isn't it? Why does the sound change if you press SHIFT? (Remember that
the computer is checking for BREAK all the time.)
3. In the traffic lights example, use the relay to control some Christmas tree lights.
More ambitiously, if your cassette recorder has a socket through which a microphone
can turn it on and off, use the relay to control that.
4. The signals that appear at the back of the circuit board are fairly standard for this
sort of Z80-based computer, but they may be in a different arrangement from another
type.
For instance, many add-ons for a Sinclair ZX81 will work on the Ace if you wire
together the corresponding terminals. The best way would be to make an adaptor
with an edge connector to fit the back of the Ace, a piece of printed circuit board to fit
in the add-on, and wires in between.
Here is a diagram of the ZX81 signals.
Top
Ml BUSRC
BUSAK
MREQ NMI
REFSH I
1
RESET |
1
WAIT
1
WR
1
no
|
IOR?
L
| HALT
INT
CM
1
D3
1
OS
1
D6
1
02
1
D1 D0 SL
| L
DT
D7
1
1 1
. ROM A4
1 1
A5 A6
1
A7
A
1
a A9
1
AIO
\
All
A12 A13 AM
1
A15
I
A3
1
A2
1
A1
|
A0
1
4
1 T
EARTH
♦9V
1
♦5V
C.S.
The RAM C.S. and ROM C.S. lines on the ZX81, and the WE on the Ace, don't need
connecting.
155
Appendix A
THE CHARACTER SET
Here is the full character set with corresponding Z80 assembler mnemonics.
Code
Character
hex
Z80 Assembler
0
new computer
00
Nop
line
1
01
Id bc,NN
2
02
Id (be),a
3
03
inc be
4
04
inc b
5
05
dec b
6
V
06
Id b,N
7
r not used
07
rlca
8
08
ex af,af
9
09
add hi,be
10
0A
Id a,(be)
11
0B
dec be
12
J
OC
inc c
13
Carriage
return
0D
dec c
14
not
used
0E
Id c,N
15
Not
used
OF
rrac
16
■
10
djnz DIS
17
L
11
Id de,NN
18
J
12
Id (de),a
19
13
inc de
20
P
14
inc d
21
1
15
deed
22
/
16
Id d,N
23
■
17
rla
24
*\
18
jr DIS
25
19
add hl,de
26
1A
Id a,(de)
27
> not used
IB
dec de
28
1C
inc e
29
ID
dec e
30
IE
Id e,N
31
y
IF
rra
—after CB —after ED
rlcb
rlc c
rlcd
rlc e
rich
rlc I
rlc (hi)
rlc a
rrc b
rrc c
rrc d
rrc e
rrc h
rrc I
rrc (hi)
rrca
rl b
rl c
rl d
rl e
rl b
rl I
rl (hi)
rl a
rr b
rr c
rr d
rr e
rr h
rr('N)
rr (hi)
rr a
156
THE CHARACTER SET
Code
Character
hex
Z80 Assembler
—after CB
—after ED
32
space
20
jr nz,DIS
sla b
33
1
21
Id hl.NN
sla c
34
ii
22
Id (NN),hl
sla d
35
#
23
inc hi
sla e
36
$
24
inch
sla h
37
%
25
dec h
sla 1
38
&
26
Id h,N
sla (hi)
39
‘
27
daa
sla a
40
(
28
jr z,DIS
sra b
41
)
29
add hi,hi
sra c
42
*
2A
Id hl,(NN)
sra d
43
+
2B
dec hi
sra e
44
5
2C
inc 1
sra h
45
-
2D
dec 1
sra 1
46
2E
Id l,N
sra (hi)
47
/
2F
cpI
sra a
48
0
30
jr nc.DIS
49
1
31
Id sp,NN
50
2
32
Id (NN),a
51
3
33
inc sp
52
4
34
inc (hi)
53
5
35
dec (hi)
55
7
37
set
56
8
38
jr c,DIS
srl b
57
9
39
add hl,sp
srl c
58
3A
Id a,(NN)
srl d
59
j
3B
dec sp
srl e
60
<
3C
inc a
srl h
61
=
3D
dec a
srl 1
62
>
3E
Id a,N
srl (hi)
63
?
3F
ccf
srl a
64
@
40
Id b,b
bit 0,b
in b,(c)
65
A
41
Id b,c
bit 0,c
out (c),b
66
B
42
Id b,d
bit 0,d
sbe hi,be
67
C
43
Id b,e
bit 0,e
Id (NN),bc
68
D
44
Id b,h
bit 0,h
neg
69
E
45
Id b,l
bit 0,1
retn
70
F
46
Id b,(hl)
bit 0,(hi)
im 0
71
G
47
Id b,a
bit 0,a
Id i,a
72
H
48
Id c,b
bit 1 ,b
in c,(c)
73
1
49
Id c,c
bit 1,c
out (c),c
74
J
4A
Id c,d
bit 1,d
adc hi,be
75
K
4B
Id c,e
bit 1 ,e
Id bc,(NN)
157
APPENDIX A
Code
Character
hex
Z80 Assembler
—after CB
—after ED
76
L
4C
Id c,h
Bit 1,h
77
M
4D
Id c, 1
Bit 1,1
reti
78
N
4E
Id c,(hl)
Bit 1,(hi)
79
0
4F
Id c,a
Bit 1 ,a
Id r,a
80
P
50
Id d,b
Bit 2,b
in d,(c)
81
Q
51
Id d,c
Bit 2,c
out (c),d
82
R
52
Id d,d
Bit 2,d
sbc hl,de
83
S
53
Id d,e
Bit 2,e
Id (NN),de
84
T
54
Id d,h
Bit 2,h
85
U
55
Id d,l
Bit 2,1
86
V
56
Id d,(hl)
Bit 2,(hi)
im 1
87
w
57
Id d,a
Bit 2,a
Id a,i
88
X
58
Id e,b
Bit 3,b
in e,(c)
89
Y
59
Id e,c
Bit 3,c
out (c),e
90
z
5A
Id e,d
Bit 3,d
adc hl,de
91
[
5B
Id e,e
Bit 3,e
Id de,(NN)
92
\
5C
Id e,h
Bit 3,h
92
93
]
5D
Id e,l
Bit 3,1
93
94
f
5E
Id e,(hl)
Bit 3,(hi)
im 2
95
5F
Id e,a
Bit 3,a
Id a,r
96
£
60
Id h,b
Bit 4,b
in h,(c)
97
a
61
Id h,c
Bit 4,c
out (c),h
98
b
62
Id h,d
Bit 4,d
sbc hi,hi
99
c
63
Id h,e
Bit 4,e
Id (NN),hl
100
d
64
Id h,h
bit 4,h
101
e
65
Id h,l
bit 4,1
102
f
66
Id h,(hl)
bit 4,(hi)
103
g
67
Id h,a
bit 4,a
rrd
104
h
68
Id l,b
bit 5,b
in l,(c)
105
i
69
Id l,c
bit 5,c
out (c), 1
106
j
6A
Id l,d
bit 5,d
adc hi,hi
107
k
6B
Id l,e
bit 5,e
Id hl,(NN)
108
1
6C
Id l,h
bit 5,h
109
m
6D
Id 1,1
bit 5,1
110
n
6E
Id l,(hl)
bit 5,(hi)
111
0
6F
Id l,a
bit 5,a
rid
112
P
70
Id (hl),b
bit 6,b
in f,(c)
113
q
71
Id (hl),c
bit 6,c
114
r
72
Id (hl),d
bit 6,d
sbc hl,sp
115
s
73
Id (hl),e
bit 6,e
Id (NN),sp
116
t
74
Id (hl),h
bit 6,h
117
u
75
Id (hi), 1
bit 6,1
118
V
76
Halt
bit 6,(hi)
119
w
77
Id (hi),a
bit 6,a
158
THE CHARACTER SET
Code
Character
hex
Z80 Assembler
—after CB
—after ED
120
X
78
Id a,b
bit 7,b
in a,(c)
121
y
79
Id a,c
bit 7,c
out (c),a
122
z
7A
Id a,d
bit 7,d
adc hl,sp
123
{
7B
Id a,e
bit 7,e
Id sp,(nn)
124
7C
Id a,h
bit 7,h
124
125
}
7D
Id a,l
bit 7,1
126
7E
Id a,(hi)
bit 7,(hi)
127
©
7F
Id a,a
bit 7,a
128
A
80
add a,b
res 0,b
129
81
add a,c
res 0,c
130
82
add a,d
res 0,d
131
83
add a,e
res 0,e
132
84
add a,h
res 0,h
133
85
add a,l
res 0,1
134
86
add a,(hi)
res 0,(hl)
135
i
87
add a,a
res 0,a
136
V not used 88
adc a,b
res 1,b
137
I
89
adc a,c
res 1 ,c
138
8A
adc a,d
res 1 ,d
139
8B
adc a,e
res 1 ,e
140
8C
adc a,h
res 1 ,h
141
8D
adc a,l
res 1,1
142
i
BE
adc a,(hi)
res 1,(hl)
143
J
8F
adc a,a
res 1 ,a
144
□
90
sub b
res 2,b
145
■
91
sub c
res 2,c
146
■
92
sub d
res 2,d
147
93
sub e
res 2,e
148
■
94
sub h
res 2,h
149
1
95
sub 1
res 2,1
150
\
96
sub (hi)
res 2,(hi)
151
n
97
sub a
res 2,a
152
>
98
sbc a,b
res 3,b
153
99
sbe a,c
res 3,c
154
9A
sbc a,d
res 3,d
155
9B
sbc a,e
res 3,e
156
> not used 9C
sbc a,h
res 3,h
157
9D
sbc a,l
res 3,1
158
9E
sbc a,(hi)
res 3,(hi)
159
j
9F
sbc a,a
res 3,a
160
□
AO
and b
res 4,b
Idi
161
inverse !
A1
and c
res 4,c
cpi
162
inverse"
A2
and d
res 4,d
ini
163
inverse #
A3
and e
res 4,e
outi
159
APPENDIX A
Code
Character
hex
Z80 Assembler
—after CB
—after ED
164
inverse $
A4
and h
res 4,h
165
inverse %
A5
and 1
res 4,1
166
inverse &
A6
and (hi)
res 4,(hi)
167
inverse'
A7
and a
res 4,a
168
inverse (
A8
xor b
res 5,b
Idd
169
inverse )
A9
xor c
res 5,c
cpd
170
inverse *
AA
xor d
res 5,d
ind
171
inverse +
AB
xor e
res 5,e
outd
172
inverse ,
AC
xor h
res 5,h
173
inverse —
AD
xor 1
res 5,1
174
inverse .
AE
xor (hi)
res 5,(hi)
175
inverse /
AF
xor a
res 5,a
176
inverse 0
BO
or b
res 6,b
Idir
177
inverse 1
B1
or c
res 6,c
cpir
178
inverse 2
B2
or d
res 6,d
inir
179
inverse 3
B3
or e
res 6,e
otir
180
inverse 4
B4
or h
res 6,h
181
inverse 5
B5
or 1
res 6,1
182
inverse 6
B6
or (hi)
res 6,(hi)
183
inverse 7
B7
or a
res 6,a
184
inverse 8
B8
cp b
res 7,b
Iddr
185
inverse 9
B9
cp c
res 7,c
cpdr
186
inverse :
BA
cp d
res 7,d
indr
187
inverse ;
BB
cp e
res 7,e
otdr
188
inverse <
BC
cp h
res 7,h
189
inverse =
BD
cp 1
res 7,1
190
inverse >
BE
cp (hi)
res 7,(hi)
191
inverse ?
BF
cp a
res 7,a
192
inverse @,
CO
ret nz
set 0,b
193
inverse A
Cl
pop be
set 0,c
194
inverse B
C2
jp nz,NN
set 0,d
195
inverse C
C3
jp NN
set 0,e
196
inverse D
C4
call nz,NN
set 0,h
197
inverse E
C5
push be
set 0,1
198
inverse F
C6
add a,N
set 0,(hl)
199
inverse G
C7
rst 0
set 0,a
200
inverse H
C8
ret z
set 1 ,b
201
inverse 1
C9
ret
set 1 ,c
202
inverse J
CA
jp z,NN
set 1 ,d
203
inverse K
CB
set 1 ,e
204
inverse L
CC
call z,NN
set 1 ,h
205
inverse M
CD
call NN
set 1,1
206
inverse N
CE
adc a,N
set 1,(hl)
207
inverse 0
CF
rst 8
set 1 ,a
160
THE CHARACTER SET
Code
Character
hex
Z80 Assembler
—after CB
208
inverse P
DO
ret nc
set 2,b
209
inverse Q
D1
pop de
set 2,c
210
inverse R
D2
jp nc,NN
set 2,d
212
inverse T
D4
call nc,NN
set 2,h
213
inverse U
D5
push de
set 2,1
214
inverse V
D6
sub N
set 2,(hi)
215
inverse W
D7
rst 16
set 2,a
216
inverse X
D8
ret c
set 3,b
217
inverse Y
D9
exx
set 3,c
218
inverse Z
DA
jp c,NN
set 3,d
219
inverse [
DB
in a,(N)
set 3,e
220
inverse \
DC
call c,NN
set 3,h
221
inverse [
DD
prefixes instructions
using ix
set 3,1
222
inverse!
DE
sbc a,N
set 3,(hi)
223
Inverse _
DF
rst 24
set 3,a
224
inverse £
EO
ret po
set 4,b
225
inverse a
El
pop hi
set 4,c
226
inverse b
E2
jp po,NN
set 4,d
227
inverse c
E3
ex (sp),hl
set 4,e
228
inverse d
E4
call po,NN
set 4,h
229
inverse e
E5
push hi
set 4,1
230
inverse f
E6
and N
set 4,(hi)
231
inverse g
E7
rst 32
set 4,a
232
inverse h
E8
ret pe
set 5,b
233
inverse i
E9
jp (hi)
set 5,c
234
inverse j
EA
jp pe,NN
set 5,d
231
inverse g
E7
rst 32
set 4,a
235
inverse k
EB
ex de,hl
set 5,e
236
inverse 1
EC
call pe,NN
set 5,h
237
inverse m
ED
set 5,1
238
inverse n
EE
xor N
set 5,(hi)
239
inverse o
EF
rst 40
set 5,a
240
inverse p
FO
ret p
set 6,b
241
inverse q
FI
pop af
set 6,c
242
inverse r
F2
jp P,NN
set 6,d
243
inverse s
F3
di
set 6,e
244
inverse t
F4
call p,NN
set 6,h
245
inverse u
F5
push af
set 6,1
246
inverse v
F6
orN
set 6,(hi)
247
inverse w
F7
rst 48
set 6,a
248
inverse x
F8
ret m
set 7,b
249
inverse y
F9
Id sp,hl
set 7,c
250
inverse z
FA
jp m,NN
set 7,d
—after ED
161
APPENDIX A
Code
Character
hex
Z80 Assembler
—after CB
251
inverse {
FB
ei
set 7,e
252
inverse 1
FC
call m,NN
set 7,h
253
inverse}
FD
prefixes instructions
using iy
set 7,1
254
inverse ~
FE
cp N
set 7,(hi)
255
inverse ©
FF
rst 56
set 7,a
—after ED
162
Appendix B
ERRORS
There are two sorts of errors. The more harmless sort occurs when the computer
finds an unrecognized word in the input buffer: it turns the cursor to H and gives you
a chance to correct it. Note that q doesn't invariably show an error; there are other
circumstances - for instance when you use EDIT - when it just means that the
computer is giving you a chance to alter the input buffer.
Other errors are more serious and cause ERROR to be shown on the screen, with
an error code. They clear the data stack, the return stack and the input buffer, and
any incomplete definition at the end of the dictionary is taken off.
Here are the various error codes, with their meanings and possible causes.
Code Meaning
1 Not enough memory. Either you tried to put too many items on the data stack
or the return stack, or you tried to put a new entry in the dictionary for which
there was not enough room. See Chapter 5.
2 Data stack underflow. The stack has apparently less than no items on it. See
Chapter 5, Exercise 4.
3 BREAK pressed. BREAK is normally shifted space; during tape operations and
BEEP it is just space.
4 You have tried to use a compiling word in interpret mode, i.e. outside a word
definition. Can be caused by IF, ELSE, THEN, BEGIN, UNTIL, WHILE,
REPEAT, DO, LOOP, +LOOP, ;, DOES>, RUNS>, (, or a compiling word
defined by COMPILER.
5 A word is not properly structured. IF ... THEN, IF . . . ELSE . .. THEN, BEGIN
. UNTIL, BEGIN ... WHILE ... REPEAT, DO ... LOOP, DO ... +LOOP,
DEFINER... DOES> ... ; and COMPILER... RUNS> . ; must all
match up and nest properly. See Chapter 10.
6 The name of a new word is either too short (no name at all) or too long (64
characters or more). Can be caused by DEFINER, COMPILER, CREATE,
CONSTANT, VARIABLE or any defining word of your own made using
DEFINER.
7 PICK or ROLL used with operand 0 or negative. Note — an operand of -32768
163
APPENDIX B
won't cause error 7. See Chapter 6, Exercise 2.
8 Overflow in floating point arithmetic: the result e a calculation is too big for the
Ace's range of floating point numbers. Car be caused by F+, F-, F* or FI. A
common cause is trying to divide by zero.
9 Trying to print in input buffer. Can be caused by AT or PLOT.
10 Tape error.
(a) In SAVE or BSAVE - either no file name supplied, nothing to save.
(b) In VERIFY or BVERIFY - the verification has failed
(c) In LOAD - the file is too .long to fit in the remaining memory.
(d) In BLOAD or BVERIFY - :the file is too long to fit in: the length specified.
(e) In LOAD, VERIFY, BLOAD or BVERIFY there was some kind of reading
fault. Either the volume setting is wrong or possibly the file is corrupted.
11 Error In REDEFINE or FORGET
In REDEFINE, either the newest word in the current vocabulary is not the
newest word of all, or the old words not found or is in ROM, or the old word
was defined by DEFINER or COMPILER but the new word wasn't, or there is
not enough space left in memory (there needs to be enough space to hold two
copies of the new word after the old one ha been, deleted).
In FORGET, the context and current vocabularies are different.
12 Incomplete definition in dictionary — certain operation;; are not then allowed.
Can be caused by REDEFINE, any tape operation, or any defining word (as
listed under Error 6). This error rectifies itself by taking the incomplete
definition out of the dictionary. You probably started a definition with [, and then
DEFINER or COMPILER), returned to execute mode with [, and then
attempted the illegal operation.
13 Word not found, or is ROM or is FORTH. Can be .caused by FORGET LIST or
EDIT.
14 Word unlistable; caused by LIST or EDIT. Only .words defined by :, DEFINER or
COMPILER are listable.
164
Appendix C
THE JUPITER ACE - FOR REFERENCE
Characters and Keyboard
The Ace uses an ASCII character set with the following special provisions:
code
meaning
0
Separates logical lines
13
Carriage return
16-23
Mosaic characters
16
■
17
L
18
a
19
y
20
r
21
E
22
0
23
E
96
£
127
©
128-255
Inverse video versions i
The shapes of all characters 0 to 127 are stored in RAM and can be redefined by
the user. Each shape is an 8 x 8 array of dots, and is stored as 8 consecutive bytes in
RAM, with the top row of dots at hex address 2C00 + 8 * ASCII code and the bottom
row at hex address 2000 + 8 * ASCII code -- 7. in each row, the leftmost dot is the
most significant bit (1 = white).
All characters, including upper and lower case can be entered from the keyboard.
SHIFT is used for capitals and control functions (shifted digits), SYMBOL SHIFT for
symbols - punctuation etc. Three special typing modes are turned oh and off
(independently of each other) by corresponding shifted digit keys: CAPS LOCK
converts lower case letters to capitals, INVERSE VIDEO converts all characters to
inverse video, and GRAPHICS converts character 310 those with codes 0 to 31 or 128
to 159, by resetting bits 5 and 6 of the ASCII code All keys repeat if held down.
The input buffer occupies the bottom of the T 'V screen and can expand upwards to
accommodate extra typing. All typing is inserted immediately to the left of the cursor,
which may change its form to reflect different ‘yping modes (normally H, 0 for
CAPS LOCK on, 0 for GRAPHICS; also Q when the computer offers a chance to
retype the buffer). The cursor can be moved about with shifted 5, 6, 7 and 8, DELETE
deletes the character just to the left of the cursor, and DELETE LINE deletes the
165
APPENDIX C
entire input buffer (but see EDIT).
When ENTER is pressed, the FORTH interpreter takes words out of the input
buffer, copies them up to the top part of the screen, and executes them (but see
INVIS). It continues until either the buffer is empty or an undefined word is found.
Shifted SPACE usually acts as BREAK, giving EF'ROR 3.
FORTH Vocabulary
In the descriptions of the words,
n means a single length integer
d means a double length integer
u means unsigned
f means a floating point number
I indicates that a word is immediate
C indicates that a word can only he used in compile mode.
!
#
#>
#S
Cl (
*
*/
*/MOD
+
Cl +LOOP
(n, address —)
Stores the single length integer n at the given address in
memory.
(udl — ud2)
Used in formatted output. Generates one digit from the
unsigned double length integer udl and holds it in the pad.
The unsigned double length integer ud2 is the quotient when
udl is divided by the number base (at BASE).
(ud — address, n)
Finishes formatted output, leaving the address and length (n)
of the resultant string.
(ud —0,0)
Applies # repeatedly (at least once) until the double length
number left on the stack is 0.
Starts a comment, terminated by ).
(n 1, n2 — nl * n2)
(n 1, n2, n3 —(nl * n2)/n3)
The intermediate product nl * n2 is held to double length.
(nl, n2, n3 — remainder, quotient of (nl * n2)/n3)
As in */, nl * n2 is held to double length.
(nl, n2 — nl +n2)
(n —)
Used with DO. Adds n to the loop counter, and loops back if
the loop counter is now less than the limit (if n 0) or greater
than the limit (if n < 0).
(n —)
Encloses the single length integer n in the dictionary.
(nl, n2 —nl — n2)
(n —)
Prints n to the television screen, followed by a space.
166
THE JUPITER ACE — FOR REFERENCE
Cl - (-)
Prints the following string, terminated by
/ (n 1, n2 — n1/n2)
Single length signed integer division.
/ MOD (n 1, n2 — remainder, quotient of n1/n2)
The remainder has the same sign as the dividend nl
0 < (n—flag)
flag is 1 if n is negative.
0 = (n—flag)
flag is 1 if n = 0.
0 > (n—flag)
flag is 1 if n is positive.
1+ (n — n+1)
1— (n —n—1)
2+ (n — n+2)
2— (n — n—2)
: Introduces colon definitions.
Cl ; Terminates colon, DEFINER and COMPILER definitions.
< (n1,n2 — flag)
flag is 1 if nl < n2.
<# (-)
Initiates formatted output.
(nl, n2 — flag)
flag is 1 if nl = n2.
> (n1,n2 — flag)
flag is 1 if nl > n2.
>R (n -)
Transfers top of data stack to return stack; it can be copied back
using I.
?DUP (n — n,n) if n * 0,
(n—n) if n=0.
@ (address — n)
Leaves on stack the single length integer at the given address.
ABORT Clears the data and return stacks, deletes any incomplete
definition left in the dictionary, prints 'ERROR' and the byte
from address 3C3D (hex) if the byte is non-negative, empties
input buffer, and returns control to the keyboard.
ABS (n — absolute value of n)
ALLOT (n —)
Encloses n bytes in the dictionary, without initializing them.
AND (nl, n2 —nl AND n2)
Bitwise Boolean operation.
ASCII Takes the next word from the input buffer, and yields the
ASCII code of its first character. If compiling, then compiles
this as a literal.
167
APPENDIX C
e g. : STARS 0 DO ASCII * EMIT LOOP ;
(—ASCII code) (if interpreting)
(—) (if compiling)
AT (line, column —)
Sets print position to line and column numbers on the stack.
There are 23 lines (0 to 22) and 32 columns (0 to 31). The
column number is taken modulo 32, and ERROR 9 if trying to
print in the input buffer at the bottom.
BASE (— 15423)
A 1-byte variable containing the system number base.
BEEP (m, n—)
Plays a note on the loudspeaker. 8*m = period in micro¬
seconds, n = time in milliseconds.
Cl BEGIN (—)
Used with either UNTIL or WHILE . .. REPEAT.
BLOAD name (m, n —)
Load at most n bytes of bytes type cassette file 'name'
starting at address m. ERROR 10 if the file has more than n
bytes.
BSAVE name (m, n —)
Save n bytes to bytes type cassette file 'name' starting at
address m.
BVERIFY name (m, n —)
Verify at most n bytes from bytes type cassette file 'name'
against RAM starting at address m. ERROR 10 if the file has
more than n bytes. For BLOAD and BVERIFY, if m = 0, then
starts at the address the bytes were saved from. If n = 0, then
doesn t care about length.
C! (n, address—)
Stores the less significant byte of n at the given address.
C, (n -)
Encloses the less significant byte of n in the dictionary.
C@ (address — byte)
Fetches the contents of the given address.
CALL (address —)
Executes Z80 machine code at address on stack. The code is
terminated by jp (iy).
e.g. in hex
DEFINER CODE DOES> CALL ;
CODE El FB C, FD C, E9 C,
The word El will enable interrupts.
CLS (-)
Clears the screen and sets the print position to top left of
screen.
Cl COMPILER Used with RUNS> for defining new compiling words, i.e.
168
THE JUPITER ACE - FOR REFERENCE
words that are used within word definitions to give an
immediate effect of compiling some information into the
dictionary. (This is traditionally done with IMMEDIATE, but
COMPILER ... RUNS> works better with EDIT etc.)
The new compiling word compiles a 2-byte address pointing
to a run-time action, and an optional operand field.
The format is
n COMPILER name
compiling routine
RUNS>
action routine
n is expected on the stack, and is the number of bytes in the
optional operand field. If it is -1, then the first two bytes of
the operand field are expected (by EDIT etc) to contain the
total length of the rest of the operand field.
'name' is the name of the new compiling word.
'compiling routine' is a piece of FORTH that will set up the
operand field — it is up to you to ensure that the number of
bytes set up equals the value of n from the stack.
'action routine' is a piece of FORTH to perform the run-time
action. It is entered with the address of the operand field on
the stack.
Note - LIST and EDIT will not list the operand fields, nor will
REDEFINE adjust them.
CONSTANT name (n —)
Defines a constant with the given name and value n.
(—15411)
A system variable pointing to the context vocabulary,
(udl.addrl—ud2, addr2)
Accumulates digits from text 'nto an unsigned double length
number udl : for each digit, the double length accumulator is
multiplied by the system number base and the digit (converted
from ASCII) is added on. The text starts at addrl + 1. addr2 is
the address of the first unconvertable character, ud2 is the
final value of the accumulator.
ri
Outputs a carriage return cha r acter to the television.
CREATE name (—)
Defines a new word with a header and empty parameter
field. When executed, the new word stacks its parameter
field address.
CURRENT (- 15409)
A system variable pointing to the current vocabulary.
D+ (d 1, d2 — dl +d2)
CONTEXT
CONVERT
CR
169
APPENDIX C
double length integer addition.
(d1,d2 —flag)
flag is 1 if of the signed double length integers, dl < d2.
(-)
sets system number base to ten.
Used with DOES> to define new defining words, i.e. words that
themselves define new words.
The format is
DEFINER name
defining routine
DOES>
action routine
'name' is the name of the new defining word; when executed
it will set up the header of a new word and use its defining
routine to set up the parameter field. When this new word
in its turn is executed, its parameter field will be put on the stack
and the action routine will be executed.
DEFINITIONS (—)
The context vocabulary is made to be current vocabulary as
well.
DNEGATE (d - -d)
Double length integer negation.
Cl DO (limit, initial value—)
Sets up a DO loop, initializing the loop counter to the initial
value. The limit and loop counter are stored on the return
stack. See LOOP and +LOOP.
Cl DOES> See DEFINER.
DROP (n —)
Throws away the top of the stack.
DUP (n —n, n)
Duplicates the top of the stack.
EDIT name (—)
Lists word 'name' at bottom of screen to be edited. Lists 18
lines at a time, then waits for editing until ENTER is pressed.
A new version of the word is entered at the end of the
dictionary.
While editing, cursor up and cursor down are needed to move
the cursor from one line to another. DELETE LINE deletes one
line.
Cl ELSE (—)
Used with IF and THEN.
EMIT (character —)
Writes the character to the television screen.
EXECUTE (compilation address —)
D<
DECIMAL
DEFINER
170
THE JUPITER ACE — FOR REFERENCE
Executes the word with the given compilation address.
EXIT (-)
Exits immediately from the word in whose definition it is
contained. Cannot be used between DO and LOOP or
+LOOP, nor between >R and R>.
F* (f 1, f2 — fl *f2)
Multiplies top two floating point numbers and leaves result on
the stack.
F+ (fl, f2 — f1+f2)
Adds top two floating point numbers.
F— (fl, f2 — fl—
Subtracts top two floating point numbers.
F. (f - )
Prints floating point number.
If 1.0E-4 < f < 1.0E9, then f is printed without an exponent
and with a decimal point in the appropriate place. If f is outside
this range, then it is printed in standard form f'En where
0 < f < 10 and -64 < n < 62.
Input may be either form, but only 6 significant figures are
accepted — further digits are ignored.
Floating point numbers are stored as 3 bytes of binary coded
decimal mantissa and 1 byte for sign and decimal exponents.
Fl (fl, f2 — f1/f2)
Divides two floating point numbers.
FAST Fast mode — runs without error checks. See SLOW.
FIND (— compilation address)
Leaves compilation address of first word in input buffer, if
defined in context vocabulary; else 0.
FNEGATE (f - -f)
Floating point negation.
FORGET name (—)
Erases the word 'name' and all subsequently defined words
from the dictionary.
FORTH (—)
Makes the standard vocabulary FORTH the context
vocabulary.
HERE (— address)
Leaves the address of one byte past the end of the dictionary.
HOLD (character —)
Used in formatted output to hold the character in the pad. I
I (— loop counter)
Copies the top of the return stack to the data stack. This will
be either the loop counter for the innermost DO ... LOOP, or
the number most recently transferred by >R.
I’ (- limit)
171
APPENDIX C
Copies second number down on return stack to data stack (so
in a DO loop, it copies the limit of the loop).
Cl IF In—)
Used in the form
IF ... THEN
or
IF... ELSE.. THEN
In the first form, if n is non-zero then the words between IF
and THEN are executed; otherwise they are skipped over.
In the second form, if n is non-zero then the words between IF
and ELSE are executed and those between ELSE and THEN
are skipped over, while if n is zero then the words between IF
and ELSE are skipped over and those between ELSE and
THEN are executed.
IMMEDIATE (—)
The most recent word in the current vocabulary is made
immediate, so that it will execute even in compile mode.
IN (port address — data byte)
Inputs a data byte from an I/O port.
INKEY (—ASCII code)
Reads the keyboard. Puts ASCII value on the stack if a key is
pressed, 0 otherwise.
INT (f— n)
Converts signed floating point number to signed single length
integer. Truncates towards zero.
INVIS Suppresses copy-up mechanism and OK. See VIS.
J (— loop counter)
Copies the third entry on the return stack to the data stack.
This will be either the loop counter for the second innermost
DO loop, or the number put on the return stack by the third
most recent >R.
LEAVE (—)
Forces termination of a DO loop at the next LOOP or +LOOP
by setting the loop counter equal to the limit.
LINE Interprets input buffer as a normal FORTH line.
LIST name (—)
Lists word 'name' on screen. It must have been defined by
DEFINER, or COMPILER. Lists about 18 lines at time and
waits for a key depression (shifted space breaks).
Cl LITERAL (n —)
Compiles the top of the stack into a word definition as a literal.
LOAD name (—)
Searches for a dictionary cassette file 'name' and loads it in,
adding it to end of old dictionary. Writes to the screen all files
found on tape. For best results, turn the tone control on the
172
THE JUPITER ACE — FOR REFERENCE
tape recorder right down (as bass as possible) and the volume
control to about three quarters maximum. See SAVE.
Cl LOOP ( — )
Like +LOOP, but the number added on to the loop counter is
1 .
MAX
MIN
MOD
NEGATE
NUMBER
OR
OUT
OVER
PAD
PICK
PLOT
QUERY
(n 1, n2 - max (n 1, n2))
Calculates the larger of two numbers.
(n 1, n2 — min (n 1, n2))
Calculates the smaller of two numbers.
(n 1, n2 — remainder n1/n2)
The remainder has the same sign as the dividend nl.
(n - -n)
Takes a number from the start of the input buffer. Leaves the
number and a non-zero address on the stack. (The address is
the compilation address of a literal compiler, so that if you then
say EXECUTE, the literal compiler compiles the number into
the dictionary as a literal — for an integer it is 4102. for a
floating point number 4181.)
If no valid number, then leaves just 0 on the stack.
(nl, n2 — nl OR n2)
Bitwise Boolean operation.
(data byte, port address —)
Outputs a data byte to an I/O port.
(nl, n2 - nl, n2, nl)
(- 9985)
Stacks the address of the 254-byte work pad.
(nl - n2)
Copies the nl-th stack entry (after dropping nl itself) to the
top. Error 7 if nl <0.
(x, y, n -)
Plots pixel (x, y) with plot mode n.
n = 0 unplot
1 plot
2 move
3 change
If n > 3, takes value modulo 4.
Clears input buffer, then accepts characters until ENTER
pressed. Buffer can be edited as usual and is limited to 22
lines.
QUIT (—)
Clears return stack, empties input buffer and returns control to
the keyboard.
R> (- entry from return stack)
Transfers top entry on return stack to data stack.
REDEFINE name (-)
Takes word 'name' and replaces it with the most recent word
173
APPENDIX C
in the dictionary. Updates entire dictionary to take changes into
account.
Most commonly used as
EDIT name
REDEFINE name
Cl REPEAT (—)
Used in construction BEGIN . . . WHILE . . . REPEAT.
Causes a jump back to just after BEGIN.
RETYPE Allows user to edit input line. Turns cursor to Q. (c.f. QUERY
which first clears input buffer.)
ROLL (n —)
Extracts the nth stack value to the top of the stack, after
dropping n itself, and moves the remaining values down to fill
the vacated position. Error 7 if n < 0.
ROT (n1,n2, n3 — n2, n3, n 1)
Cl RUNS> See COMPILER.
SAVE name (—)
Saves the entire dictionary in RAM on a dictionary type
cassette file with the given name. Makes a noise on the
internal loudspeaker. See VERIFY and LOAD, and also BSAVE,
BVERIFY and BLOAD.
SIGN (n —)
In formatted output, holds a minus sign in the pad if n is
negative.
SLOW (—)
Slow mode with error checking. See FAST.
SPACE (—)
EMITs a space.
SPACES (n —)
EMITs n spaces, if n > 1.
SWAP (n 1, n2 — n2, nl)
Cl THEN Used with IF.
TYPE (address, n —)
EMITs n characters from memory starting at the address.
U* (uni, un2 — double length (uni * un2))
Multiplies two unsigned single length integers together to
give an unsigned double length product.
U. (un —)
Prints the unsigned single length integer un to the television
screen, followed by a space.
U/MOD (ud1,un2 — un3, un4)
In unsigned arithmetic throughout, divides the double length
integer udl by the single length integer un2 to give a single
length remainder un3 and a single length quotient un4.
U< (un1,un2 — flag)
174
THE JUPITER ACE — FOR REFERENCE
The flag is 1 if of the two unsigned single length integers uni
is less than un2.
U FLOAT (un—f)
Converts unsigned, single length integer to floating point.
Cl UNTIL (n—)
Used in BEGIN . . . UNTIL. Loops back to BEGIN if n = 0.
VARIABLE name
(n —)
Sets up a variable with the given name, and initializes its value
to n.
VERIFY name (—)
Verifies dictionary on tape against dictionary in RAM. See
SAVE.
VIS Allows copy-up mechanism and OK. See INVIS.
VLIST List dictionary to screen, including words in ROM. (No pause
after 18 lines.)
VOCABULARY name
(-)
Defines a new vocabulary with the given name.
Cl WHILE (n —)
Used in BEGIN ... WHILE . . . REPEAT. If n = 0 then skips
over to just past REPEAT.
WORD text (delimiter — address)
Takes text out of the input buffer up as far as a delimiter, and
copies it to pad, starting at the second byte there. Puts the
length (not including delimiter) in the first byte of the pad, and
stacks the address of the first byte of the pad.
At most 253 characters are taken from the input buffer. If
there are more left before the delimiter, then the first byte of
the pad shows 254. Initial delimiters are ignored.
XOR (n 1, n2 — nl XOR n2)
Bitwise Boolean XOR (exclusive or).
I [ (-)
Enters interpret mode.
] (-)
Enters compile mode.
175
Appendix D
QUICK GUIDE FOR 'FORTH' ENTHUSIASTS
Ace FORTH is based on FORTH-79, the principle differences being:
1. The Ace does not use screens at all. Input and output uses a cassette recorder,
and stores either a dictionary in its compiled form (lists of compilation addresses) or
raw bytes from memory. See SAVE, VERIFY, LOAD, BSAVE, BVERIFY, BLOAD.
2. The Ace can decompile words as a matter of course — see LIST and EDIT. It can
also change already compiled words in the dictionary and adjust all the compilation
addresses and pointers - see REDEFINE and LOAD.
3. There are some facilities for floating point arithmetic — F+, F—, F*. FI, F.,
FNEGATE, INT, UFLOAT.
4. DEFINER . . DOES> replaces : . CREATE ... DOES> for defining new
defining words; there is a corresponding pair COMPILER ... RUNS> for defining
new compiling words.
5. There are extra words ASCII, AT, BEEP, CALL, CLS, FAST, IN, INKEY, INVIS,
LINE NUMBER, OUT, PLOT, RETYPE, SLOW, VIS.
6. Ace FORTH lacks \ +!, -TRAILING, 79-STANDARD, >IN, ?, CMOVE,
COMPILE, COUNT, DEPTH, EXPECT, FILL, KEY, MOVE, NOT, STATE,
[COMPILE].
176
Index
!
42, 73
—
21
ii
17, 125
#
112
0
8
#>
112
0<
49
#S
112
0=
49
i
122
0>
49
(
29
1
8
)
29
1+
23
*
21,111
1-
23
*/
23, 111
2!
91
*/MOD
23, 111
2+
23
+
19
2-
23
+!
44
2@
91
+LOOP
58
2CONSTANT
122
J
118
2DROP
91
-
21
20VER
91
29
2PICK
91
"trailing
100
2ROLL
91
19
2 ROT
91
»»
17
2SWAP
91
.s
143
2VARIABLE
122
/
21, 111
/MOD
22, 111
;
16
A
J
16, 120, 137
ABORT
96, 144, 145, 148
<
48, 104
ABS
23, 114
<#
112
accumulator
114
<BUILDS
121
action
120
—
48
addition
19, 89, 110
>
48
address
43, 150
>R
61
ALLOT
119
?
44
alphabetical order
125
?DUP
50
AND
107
@
42, 73
array
117, 124
[
134
ASCII
69, 73, 108
]
134
assembly language
146
t
63
AT
74
177
INDEX
B
BASE
102,144
Batten berg cake
69
BEEP
64
BEGIN
55
binary
102, 108
binary coded decimal
115
bit
101, 108
BLOAD
15, 86
BOLD type
9
Boolean
107
brackets
29, 34, 134
BREAK
17,100
BSAVE
86
buffer
9, 95
bus
150
byte
43
Bytes:
86
C
s
12
C!
73
c,
118
c@
73
CALL
146
CAPS LOCK
12
carriage return
17, 69
cassette tape
13, 84
character
8,69
character set
69, 87, 140
clock
142
CLS
74
CMOVE
130
code
146
code field
117, 149
colon
16
comment
29, 38
comparison of strings
29, 38
compilation address
97, 122, 136
compile
134
compiled word
137
COMPILER
136
compile-time
136
compiling word
136
complement
104, 106, 109
computer file
35, 141
computer program
16
condition
47, 55, 107
CONSTANT
41
CONTEXT
132, 133, 143
control character
69
CONVERT
114
coordinate
77
COS
93
COUNT
99
counter
57
CPU
146
CR
17
CREATE
117
CURRENT
132, 133, 143
cursor
11,35
D
D+
110
D.
113
D.R
116
D<
111
D0<
114
D0=
114
DABS
114
data stack
61
DECIMAL
102
decimal point
89
decision
46
DEFINER
120
defining word
121
definition - character
71, 105
definition - colon
16, 134
DEFINITIONS
132
DELETE
11
DELETE LINE
11,36
delimiter
98
Diet:
14, 86
dictionary 16,
36, 84, 117, 131
digit
8, 101, 111
dimension
127
division
21,89, 111
DNEGATE
111
DO
57
DOES>
120
178
INDEX
double length
110
DRAW
79
DROP
28
DUP
27
E
EAR
89
edge connector
154
EDIT
35
element
127
ELSE
47
EMIT
69
enclose
118
ENTER
8, 36
ERROR
14, 16, 25 148
error number
144
EXECUTE
17, 97, 136
EXIT
61
exponent
89,115
F
F*
89
F+
89
F-
89
F.
89
FI
89
false
47
FAST
148
field
117, 121
file
86
FILL
130
FIND
97
flag
48
floating point
89
flowchart
46
FNEGATE
89
FORGET
39
formatted output
111,141
FORTH
131
fraction
89
G
E
12, 70
GR
71
GRAPHICS
12, 70, 77
H
header
117
HERE
139
HEX
106
hexadecimal
102
HOLD
112
1
8
1
57
r
59
I/O port
150
IF
47
IMMEDIATE
122, 135
IN
150
infinite loops
63
initial value
67
INKEY
94
INPUT
95
input/output port
150
input buffer
8, 95
INT
8, 95
integer
21,43, 89, 110
interpret
134
INVERSE VIDEO
12, 70
INVIS
79
invisible
79, 96
J
59
K
106
KEY
94
keyboard
8, 94
L
LEAVE
61
length field
121
limit
57
LINE
95
link
117, 132
linkage
133, 143
LIST
34
listing
34
LOAD
14, 85
logical line
141
LOOP
57
179
M
machine code
146
mask
109
MAX
23
memory
43, 72, 140
memory mapping
154
MIC
13
MIN
23
mistakes
11,35
mnemonic
146
MOD
22
module
22
MOVE
130
multiplication
21,89, 111
music
64
N
name
117
name length field
121
NEGATE
23
nesting
59
Newton-Raphson
92
NUMBER
96
0
8
octal
105
OK
9, 16, 39
ones complement
106
operand
27
operand field
137
OR
107
OUT
150
output
142
OVER
30
P
PAD
97, 112
parameter
117
parent vocabulary
131
peripheral
150
PICK
30
pitch number
64, 122
pixel
77
PLOT
77
plotting mode
77
POWER
6
power
44, 63
prime number
62
primitive
148
printer
142
processor
146
program
16
pseudorandom number
82
Q
QUERY
95
question mark
9, 18, 35, 95
QUIT
96
quotient
22
R
R>
61
raising to a power
44,63
RAM
43, 140
RAND
83
random number
82
recursion
52
REDEFINE
35
REPEAT
56
restart
147
result
27
return stack
61
RETYPE
95
RND
83
ROLL
31
ROM
43, 140, 146
ROT
30
run-time
136
RUNS>
137
S
S—>D
116
SAVE
84
scientific notation
89
semicolon
16, 120, 137
semitone
66, 122
SHIFT
8
SIGN
113
signed
104,110
significant
109
180
INDEX
SIN
92
U.
104
single length
109
U/MOD
110
slice
125
U<
104
SLOW
148
UFLOAT
90
sound
64
underflow - stack
25
SPACE
10, 74
underline
21
SPACES
74
unsigned
104, 110
SORT
92
UNTIL
55
stack
19, 26, 61, 143,147
step
58
V
string
124
value of a variable
42
subscript
127
VARIABLE
42
substring
125
variable - system
81, 102, 140
subtraction
21,89
VERIFY
84
SWAP
27
Video RAM
140
switch - on/off
6
VIS
79
SYMBOL SHIFT
8
visible
79, 96
System variable
81, 102, 140
VLIST
9, 132
VOCABULARY
131
T
TAB
75
W
TAN
93
WHILE
56
tape
13, 84
WORD
9, 97, 117
television
86
testing word
48
X
THEN
47
x-coordinate
77
tick
122
XOR
107
timer
142
TLA
118
true
47
Y
truth table
107
Y/N
94
TV
6
y-coordinate
77
twos complement
104
TYPE
74, 99
Z
U
Z80
25
u*
110
ZX81
155
181
The computer language FORTH is becoming widely available on
microcomputers, for which many remarkable features make it ideal. With
FORTH you can easily write faster, more effective programs.
This book, a course in FORTH programming written for the Jupiter Ace
and provided free with it, can also be used on its own to explain clearly and
readably how to use this intriguing language.
Printed by
The Leagrave Press Ltd
Luton and London
182